[Jifty-commit] r4253 - in jifty/trunk: lib/Jifty/Plugin/OAuth
lib/Jifty/Plugin/OAuth/Model
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Wed Oct 17 18:08:31 EDT 2007
Author: sartak
Date: Wed Oct 17 18:08:30 2007
New Revision: 4253
Added:
jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm
Modified:
jifty/trunk/ (props changed)
jifty/trunk/Makefile.PL
jifty/trunk/lib/Jifty/Plugin/OAuth/Dispatcher.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm
Log:
r43829 at onn: sartak | 2007-10-17 18:08:12 -0400
Some more fleshing out of the OAuth plugin
Modified: jifty/trunk/Makefile.PL
==============================================================================
--- jifty/trunk/Makefile.PL (original)
+++ jifty/trunk/Makefile.PL Wed Oct 17 18:08:30 2007
@@ -165,6 +165,12 @@
recommends('Devel::Events::Generator::Objects'),
recommends('Devel::Size'),
],
+ 'OAuth Plugin' => [
+ -default => 0,
+ recommends('Net::OAuth::Request'),
+ recommends('Crypt::OpenSSL::RSA'),
+ recommends('Digest::HMAC_SHA1'),
+ ],
);
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 Wed Oct 17 18:08:30 2007
@@ -4,6 +4,10 @@
use Jifty::Dispatcher -base;
+use Net::OAuth::RequestTokenRequest;
+use Net::OAuth::AccessTokenRequest;
+use Net::OAuth::ProtectedResourceRequest;
+
my $request_token_url = '/oauth/request_token';
my $authorize_url = '/oauth/authorize';
my $access_token_url = '/oauth/access_token';
@@ -14,60 +18,121 @@
# a consumer wants a request token
sub request_token {
- set oauth_url => $request_token_url;
- my $headers = Jifty->web->handler->apache->headers_in();
+ 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});
+
+ # Net::OAuth::Request will die hard if it doesn't get everything it wants
+ my $request = eval { Net::OAuth::RequestTokenRequest->new(
+ request_url => $request_token_url,
+ request_method => Jifty->handler->apache->method(),
+ consumer_secret => $consumer->secret,
+
+ map { $_ => $oauth_params{$_} } @params
+ ) };
+
+ abort(400) if $@ || !defined($request);
+
+ # make sure the signature matches the rest of what the consumer gave us
+ abort(401) unless $request->verify;
- for my $necessary_header (map {"oauth_$_"}
- qw/consumer_key signature_method signature
- timestamp nonce/) {
- abort(400) if !defined $headers->{$necessary_header};
- }
+ # ok, everything checks out. send them back a request token
+ # at this point, the only things that could go wrong are:
+ # 1) we've already seen this nonce and timestamp. possibly a replay attack,
+ # so we abort
+ # 2) we tried a bunch of times to create a unique token but failed. abort
+ # because we don't have any other option
+ my $token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
+
+ my ($ok) = eval {
+ $token->create(map { $_ => $oauth_params{$_} } qw/timestamp nonce/);
+ };
+
+ abort(401) if $@ || !defined($token) || !$ok;
+
+ # XXX: actually send the token
}
# the user is authorizing (or denying) a consumer's request token
sub authorize {
- set oauth_url => $authorize_url;
}
# the consumer is trying to trade a request token for an access token
sub access_token {
- set oauth_url => $access_token_url;
+ my @params = qw/consumer_key signature_method signature
+ timestamp nonce token token_secret version/;
-}
+ my %oauth_params = get_parameters(@params);
+ validate_signature_method($oauth_params{signature_method});
+ my $consumer = get_consumer($oauth_params{consumer_key});
+
+ # Net::OAuth::Request will die hard if it doesn't get everything it wants
+ my $request = eval { Net::OAuth::AccessTokenRequest->new(
+ request_url => $request_token_url,
+ request_method => Jifty->handler->apache->method(),
+ consumer_secret => $consumer->secret,
-# 9.1.1
-sub get_normalized_parameters {
- my $parameters = Jifty->handler->apache->headers_in();
- my @parameters;
+ map { $_ => $oauth_params{$_} } @params
+ ) };
- # we can't just use a hash because parameters may be repeated
- $parameters->do(sub {
- my ($key, $value) = @_;
- push @parameters, [$key, defined($value) ? $value : ''];
- return 1;
- });
+ abort(400) if $@ || !defined($request);
- # XXX: include query parameters (http://x.com/path?THIS=THAT)
+ # make sure the signature matches the rest of what the consumer gave us
+ abort(401) unless $request->verify;
- for (@parameters) {
- @$_ = map { Jifty->web->escape_uri($_) } @$_;
- }
+ # is the request token they're using still valid?
+ my $request_token = Jifty::Plugin::OAuth::Model::RequestToken->new(current_user => Jifty::CurrentUser->superuser);
+ $request_token->load_by_cols(consumer => $consumer, token => $oauth_params{token}, token_secret => $oauth_params{token_secret});
+ abort(401) unless $request_token->id;
+ abort(401) unless $request_token->can_trade_for_access_token;
- return join '&',
- map { "$_->[0]=$_->[1]" }
- grep { $_->[0] ne 'oauth_signature' }
- sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } @parameters;
+ my $token = Jifty::Plugin::OAuth::Model::AccessToken->new(current_user => Jifty::CurrentUser->superuser);
+
+ my ($ok) = eval {
+ $token->create(consumer => $consumer,
+ user => $request_token->authorized_by,
+ map { $_ => $oauth_params{$_} } qw/timestamp nonce/);
+ };
+
+ abort(401) if $@ || !defined($token) || !$ok;
+
+ # XXX: actually send the token
}
-# 9.1.2
-sub get_request_elements {
- my $method = uc Jifty->handler->apache->method();
- my $url = Jifty->web->url(get 'oauth_url');
- my $parameters = get_normalized_parameters();
- my $consumer_secret = 'todo';
- my $token_secret = 'todo' || '';
+sub get_consumer {
+ my $key = shift;
+ my $consumer = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
+ $consumer->load_by_cols(key => $key);
+ abort(401) if !$consumer->id;
+ return $consumer;
+}
+
+my %valid_signature_methods = map { $_ => 1 } qw/PLAINTEXT HMAC-SHA1 RSA-SHA1/;
+sub validate_signature_method {
+ my $method = shift;
+ abort(400) unless $valid_signature_methods{$method};
+}
+
+sub get_parameters {
+ my %p;
+
+ # XXX: Check Authorization header
+
+ %p = ((map {
+ my $v = Jifty->handler->apache->header_in("oauth_$_");
+ defined $v ? ($_ => $v) : ()
+ } @_), %p);
+
+ # XXX: Check query string
+
+ $p{version} ||= '1.0';
+ abort(400) if grep { !defined($p{$_}) } @_;
+ return %p;
}
1;
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Model/AccessToken.pm Wed Oct 17 18:08:30 2007
@@ -3,7 +3,7 @@
use strict;
use warnings;
-use base qw( Jifty::Record );
+use base qw( Jifty::Plugin::OAuth::Token Jifty::Record );
# kludge 1: you cannot call Jifty->app_class within schema {}
my $app_user;
@@ -21,12 +21,19 @@
type is 'timestamp',
filters are 'Jifty::DBI::Filter::DateTime';
-};
+ column token_secret =>
+ type is 'varchar';
+
+ column consumer =>
+ refers_to Jifty::Plugin::OAuth::Model::Consumer;
-sub before_create {
- my ($self, $attr) = @_;
- $attr{valid_until} ||= DateTime->now->add(hours => 1);
-}
+ # we use these to make sure we aren't being hit with a replay attack
+ column timestamp =>
+ type is 'integer';
+
+ column nonce =>
+ type is 'varchar';
+};
1;
Added: jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Model/Consumer.pm Wed Oct 17 18:08:30 2007
@@ -0,0 +1,35 @@
+#!/usr/bin/env perl
+package Jifty::Plugin::OAuth::Model::Consumer;
+use strict;
+use warnings;
+
+use base qw( Jifty::Record );
+
+use Jifty::DBI::Schema;
+use Jifty::Record schema {
+
+ # the unique key that identifies a consumer
+ column key =>
+ type is 'varchar',
+ is distinct,
+ is required;
+
+ # a secret used in signing to verify that we have the real consumer (and
+ # not just someone who got ahold of the key)
+ column secret =>
+ type is 'varchar',
+ is required;
+
+ # the name of the consumer, e.g. Bob's Social Network
+ column name =>
+ type is 'varchar',
+ is required;
+
+ # the url of the consumer, e.g. http://social.bob/
+ column url =>
+ type is 'varchar';
+
+};
+
+1;
+
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Model/RequestToken.pm Wed Oct 17 18:08:30 2007
@@ -3,7 +3,7 @@
use strict;
use warnings;
-use base qw( Jifty::Record );
+use base qw( Jifty::Plugin::OAuth::Token Jifty::Record );
# kludge 1: you cannot call Jifty->app_class within schema {}
my $app_user;
@@ -25,34 +25,39 @@
type is 'integer';
#refers_to $app_user;
+ column consumer =>
+ refers_to Jifty::Plugin::OAuth::Model::Consumer;
+
column used =>
type is 'boolean',
default is 'f';
-};
+ column token =>
+ type is 'varchar';
-sub before_create {
- my ($self, $attr) = @_;
- $attr{valid_until} ||= DateTime->now->add(hours => 1);
-}
+ column token_secret =>
+ type is 'varchar';
+
+ # we use these to make sure we aren't being hit with a replay attack
+ column timestamp =>
+ type is 'integer';
+
+ column nonce =>
+ type is 'varchar';
+};
sub set_authorized {
my $self = shift;
$self->set_authorized_by(Jifty->web->current_user->id);
}
-sub trade_for_access_token {
+sub can_trade_for_access_token {
my $self = shift;
- return undef if !$self->authorized;
- return undef if !$self->authorized_by;
- return undef if $self->used;
- return undef if $self->valid_until < DateTime->now;
-
- my $access_token = Jifty::Plugin::OAuth::Model::AccessToken->new(current_user => Jifty::CurrentUser->superuser);
- my ($ok, $msg) = $access_token->create(user => $self->authorized_by);
-
- return undef if !$ok;
- return $access_token;
+ return 0 if !$self->authorized;
+ return 0 if !$self->authorized_by;
+ return 0 if $self->used;
+ return 0 if $self->valid_until < DateTime->now;
+ return 1;
}
1;
Added: jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm
==============================================================================
--- (empty file)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Token.pm Wed Oct 17 18:08:30 2007
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+package Jifty::Plugin::OAuth::Token;
+use strict;
+use warnings;
+
+# this just provides some helper methods for both token classes to use
+
+sub generate_token {
+ return join '', map { unpack('H2', chr(int rand 256)) } 1..10;
+}
+
+sub before_create {
+ my ($self, $attr) = @_;
+
+ # check if we're seeing a replay attack
+ my $token = $self->new(current_user => Jifty::CurrentUser->superuser);
+ $token->load_by_cols(nonce => $attr->{nonce}, timestamp => $attr->{nonce});
+ return if $token->id;
+
+ # attempt 20 times to create a unique token string
+ for (1..20) {
+ $attr->{token} = generate_token();
+ my $token = $self->new(current_user => Jifty::CurrentUser->superuser);
+ $token->load_by_cols(token => $attr->{token});
+ last if !$token->id;
+ delete $attr->{token};
+ }
+ return if !defined($attr->{token});
+
+ # generate a secret. need not be unique, just hard to guess
+ $attr->{token_secret} = generate_token();
+
+ # default the lifetime of this token to 1 hour
+ $attr->{valid_until} ||= DateTime->now->add(hours => 1);
+
+ return 1;
+}
+
+1;
+
More information about the Jifty-commit
mailing list