[Jifty-commit] r5154 - in jifty/trunk: . lib/Jifty/Plugin lib/Jifty/Plugin/OAuth lib/Jifty/Plugin/OAuth/Action lib/Jifty/Plugin/OAuth/Model t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth t/TestApp-Plugin-OAuth/t
Jifty commits
jifty-commit at lists.jifty.org
Wed Feb 20 23:59:14 EST 2008
Author: sartak
Date: Wed Feb 20 23:59:13 2008
New Revision: 5154
Added:
jifty/trunk/t/TestApp-Plugin-OAuth/t/06-read-only.t
Modified:
jifty/trunk/ (props changed)
jifty/trunk/lib/Jifty/Plugin/OAuth.pm
jifty/trunk/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm
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
jifty/trunk/lib/Jifty/Plugin/OAuth/View.pm
jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm
jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Model/User.pm
jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm
Log:
r51986 at onn: sartak | 2008-02-20 23:58:57 -0500
OAuth Checkpoint:
* User has some choice how long to grant access (instead of only 1 hour)
* User may now choose to deny the consumer write access (the current_user_can check isn't working just yet)
* Add CurrentUser->oauth_token
* Test fixes so that http methods other than GET and POST work
* TestApp::OAuth::User->current_user_can
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth.pm Wed Feb 20 23:59:13 2008
@@ -7,7 +7,29 @@
our $VERSION = 0.01;
sub init {
- Jifty::CurrentUser->mk_accessors(qw(is_oauthed));
+ Jifty::CurrentUser->mk_accessors(qw(is_oauthed oauth_token));
+
+ Jifty::Record->add_trigger(before_access => sub {
+ my $record = shift;
+ my $right = shift;
+
+ # not oauthed? usual rules
+ warn $record->current_user->id;
+ $record->current_user->is_oauthed
+ or return 'ignore';
+
+ my $token = $record->current_user->oauth_token;
+
+ # read access? usual rules
+ $right eq 'read'
+ or return 'ignore';
+
+ # if the token does not give write access, then WE DO NOT HAVE IT
+ return 'deny' unless $token->__value('can_write');
+
+ # we have not been forbidden from updating, so: usual rules
+ return 'ignore';
+ });
}
=head1 NAME
@@ -20,9 +42,9 @@
and limited access to your users' data.
This plugin adds an C</oauth> set of URLs to your application, listed below. It
-also adds C<is_oauthed> to L<Jifty::CurrentUser>, so you may have additional
-restrictions on OAuth access (such as forbidding OAuthed users to change users'
-passwords).
+also adds C<is_oauthed> and C<oauth_token> to L<Jifty::CurrentUser>, so you may
+have additional restrictions on OAuth access (such as forbidding OAuthed users
+to change users' passwords).
=head2 /oauth
@@ -185,7 +207,9 @@
=head2 init
-This adds an is_oauthed accessor to L<Jifty::CurrentUser>.
+This adds an is_oauthed accessor to L<Jifty::CurrentUser>. It also establishes
+a trigger in L<Jifty::Record> so that only OAuthed consumers with write access
+can do anything other than read.
=head1 SEE ALSO
Modified: jifty/trunk/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm (original)
+++ jifty/trunk/lib/Jifty/Plugin/OAuth/Action/AuthorizeRequestToken.pm Wed Feb 20 23:59:13 2008
@@ -25,6 +25,23 @@
param 'callback',
render as 'hidden';
+ param 'use_limit',
+ label is 'Use limit',
+ hints are 'How long should the site have access?',
+ render as 'select',
+ default is '1 hour',
+ valid_values are (
+ '5 minutes',
+ '1 hour',
+ '1 day',
+ '1 week',
+ );
+
+ param 'can_write',
+ label is 'Write access?',
+ hints are 'Should the site be allowed to update your data? (unchecking restricts to read-only)',
+ render as 'checkbox',
+ default is 0;
};
=head2 validate_token
@@ -69,10 +86,18 @@
$self->result->content(token_obj => $token);
$self->result->content(token => $token->token);
- $self->result->content(callback => $self->argument_value('callback'));
+
+ for (qw/callback use_limit can_write/) {
+ $self->result->content($_ => $self->argument_value($_));
+ }
if ($self->argument_value('authorize') eq 'allow') {
$token->set_authorized(1);
+ $token->set_access_token_restrictions({
+ can_write => $self->argument_value('can_write'),
+ use_limit => $self->inflate_use_limit,
+ });
+
$self->result->message("Allowing " . $token->consumer->name . " to access your stuff.");
}
else {
@@ -83,5 +108,26 @@
return 1;
}
+=head2 inflate_use_limit -> DateTime
+
+Takes the use_limit argument and inflates it to a DateTime object representing
+when the access token will expire. It expects the input to be of the form
+"number_of_periods period_length", so "5 minutes", "1 hour", etc.
+
+=cut
+
+sub inflate_use_limit {
+ my $self = shift;
+ my $use_limit = $self->argument_value('use_limit');
+
+ my ($periods, $length) = $use_limit =~ m{^(\d+)\s+(\w+)$}
+ or die "AuthorizeRequestToken->inflate_use_limit failed to parse input $use_limit";
+
+ # DateTime::Duration accepts only plurals
+ $length .= 's' if $periods == 1;
+
+ return DateTime->now->add($length => $periods);
+}
+
1;
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 Feb 20 23:59:13 2008
@@ -182,12 +182,7 @@
# make sure the signature matches the rest of what the consumer gave us
abortmsg(401, "Invalid signature (type: $oauth_params{signature_method}).") unless $request->verify;
- my $token = Jifty::Plugin::OAuth::Model::AccessToken->new(current_user => Jifty::CurrentUser->superuser);
-
- ($ok, $msg) = eval {
- $token->create(consumer => $consumer,
- auth_as => $request_token->authorized_by);
- };
+ my $token = Jifty::Plugin::OAuth::Model::AccessToken->create_from_request_token($request_token);
abortmsg(401, "Unable to create an Access Token: " . $@ || $msg)
if $@ || !defined($token) || !$ok;
@@ -262,8 +257,13 @@
$consumer->made_request(@oauth_params{qw/timestamp nonce/});
- Jifty->web->temporary_current_user(Jifty->app_class('CurrentUser')->new(id => $access_token->auth_as));
- Jifty->web->current_user->is_oauthed($access_token);
+ my $new_current_user = Jifty->app_class('CurrentUser')->new(
+ id => $access_token->auth_as,
+ );
+ $new_current_user->is_oauthed(1);
+ $new_current_user->oauth_token($access_token);
+
+ Jifty->web->temporary_current_user($new_current_user);
Jifty->log->info("Consumer " . $consumer->name . " successfully OAuthed as user ". $access_token->auth_as);
}
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 Feb 20 23:59:13 2008
@@ -35,6 +35,9 @@
column consumer =>
refers_to Jifty::Plugin::OAuth::Model::Consumer;
+ column can_write =>
+ type is 'boolean',
+ default is '0';
};
=head2 table
@@ -45,6 +48,34 @@
sub table {'oauth_access_tokens'}
+=head2 create_from_request_token
+
+This creates a new access token (as the superuser) and populates its values
+from the given request token.
+
+=cut
+
+sub create_from_request_token {
+ my $self = shift;
+ my $request_token = shift;
+
+ if (!ref($self)) {
+ $self = $self->new(current_user => Jifty::CurrentUser->superuser);
+ }
+
+ my $restrictions = $request_token->access_token_restrictions
+ or die "No access-token restrictions given in the request token.";
+
+ $self->create(
+ consumer => $request_token->consumer,
+ auth_as => $request_token->authorized_by,
+ valid_until => $restrictions->{use_limit},
+ can_write => $restrictions->{can_write} ? 1 : 0,
+ );
+
+ return $self;
+}
+
=head2 is_valid
This neatly encapsulates the "is this access token perfect?" check.
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 Feb 20 23:59:13 2008
@@ -45,6 +45,9 @@
type is 'varchar',
is required;
+ column access_token_restrictions =>
+ type is 'blob',
+ filters are 'Jifty::DBI::Filter::Storable';
};
=head2 table
@@ -65,7 +68,6 @@
sub after_set_authorized {
my $self = shift;
$self->set_authorized_by(Jifty->web->current_user->id);
- $self->set_valid_until(DateTime->now->add(hours => 1));
}
=head2 can_trade_for_access_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 Wed Feb 20 23:59:13 2008
@@ -143,6 +143,9 @@
$authorize->form_field('token')->render;
}
+ $authorize->form_field('use_limit')->render;
+ $authorize->form_field('can_write')->render;
+
outs_raw $authorize->hidden(callback => get 'callback');
outs_raw($authorize->button(
@@ -209,9 +212,7 @@
div {
p {
show '/oauth/consumer';
- outs ' is trying to access your data on this site. If you trust this application, you may grant it access. Note that ';
- strong { "access is unrestricted" };
- outs ' and will expire in one hour after you click "Allow".';
+ outs ' is trying to access your data on this site. If you trust this application, you may grant it access.';
}
p {
"If you're at all uncomfortable with the idea of someone rifling through your things, or don't know what this is, click Deny."
Modified: jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm (original)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Dispatcher.pm Wed Feb 20 23:59:13 2008
@@ -6,6 +6,7 @@
my @login_required = qw{
oauth/authorize
nuke/?
+ =
};
my $login_required = join '|', map {"^$_"} @login_required;
Modified: jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Model/User.pm
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Model/User.pm (original)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Model/User.pm Wed Feb 20 23:59:13 2008
@@ -14,5 +14,16 @@
use Jifty::Plugin::User::Mixin::Model::User;
use Jifty::Plugin::Authentication::Password::Mixin::Model::User;
+sub current_user_can {
+ my $self = shift;
+
+ return 1 if $_[0] eq 'create';
+
+ my $id = $self->__value('id');
+ return 1 if $id == $self->current_user->id;
+
+ $self->SUPER::current_user_can(@_);
+}
+
1;
Modified: jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm
==============================================================================
--- jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm (original)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/lib/TestApp/Plugin/OAuth/Test.pm Wed Feb 20 23:59:13 2008
@@ -82,16 +82,26 @@
$cmech->default_header("Authorization" => authz(%params));
}
- if ($method eq 'POST') {
- $r = $cmech->post($url, $params_in eq 'method' ? [%params] : ());
- }
- else {
+ if ($method eq 'GET') {
my $query = join '&',
map { "$_=" . Jifty->web->escape_uri($params{$_}||'') }
keys %params;
my $params = $params_in eq 'method' ? "?$query" : '';
$r = $cmech->get("$url$params");
}
+ else {
+ my $req = HTTP::Request->new(
+ uc($method) => $url,
+ );
+
+ if ($params_in eq 'method') {
+ my $content = Jifty->web->query_string(%params);
+ $req->header('Content-type' => 'application/x-www-form-urlencoded');
+ $req->content($content);
+ }
+
+ $r = $cmech->request($req);
+ }
$cmech->default_headers->remove_header("Authorization");
Added: jifty/trunk/t/TestApp-Plugin-OAuth/t/06-read-only.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp-Plugin-OAuth/t/06-read-only.t Wed Feb 20 23:59:13 2008
@@ -0,0 +1,99 @@
+#!/usr/bin/env perl
+use warnings;
+use strict;
+
+use Test::More;
+BEGIN {
+ if (eval { require Net::OAuth::Request; require Crypt::OpenSSL::RSA; 1 }) {
+ plan tests => 58;
+ }
+ else {
+ plan skip_all => "Net::OAuth or Crypt::OpenSSL::RSA isn't installed";
+ }
+}
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use TestApp::Plugin::OAuth::Test;
+
+use Jifty::Test::WWW::Mechanize;
+
+# setup {{{
+# create two consumers {{{
+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',
+ rsa_key => $pubkey,
+);
+ok($ok, $msg);
+
+my $rsaless = Jifty::Plugin::OAuth::Model::Consumer->new(current_user => Jifty::CurrentUser->superuser);
+($ok, $msg) = $rsaless->create(
+ consumer_key => 'foo2',
+ secret => 'bar2',
+ name => 'Backwater.org',
+ url => 'http://backwater.org',
+);
+ok($ok, $msg);
+# }}}
+# create user and log in {{{
+my $u = TestApp::Plugin::OAuth::Model::User->new(current_user => TestApp::Plugin::OAuth::CurrentUser->superuser);
+$u->create( name => 'You Zer', email => 'youzer at example.com', password => 'secret', email_confirmed => 1);
+my $uid = $u->id;
+ok($uid, "New user has valid id set");
+
+$umech->get_ok($URL . '/login');
+$umech->fill_in_action_ok($umech->moniker_for('TestApp::Plugin::OAuth::Action::Login'), email => 'youzer at example.com', password => 'secret');
+$umech->submit;
+$umech->content_contains('Logout');
+# }}}
+# }}}
+# make sure we're not logged in {{{
+response_is(
+ url => '/nuke/the/whales',
+ code => 200,
+ testname => "200 - protected resource request",
+ consumer_secret => 'bar',
+ oauth_consumer_key => 'foo',
+ oauth_signature_method => 'PLAINTEXT',
+ oauth_token => 'please',
+ token_secret => 'letmein',
+);
+$cmech->content_contains("Login with a password", "redirected to login");
+$cmech->content_lacks("Press the shiny red button", "did NOT get to a protected page");
+# }}}}
+# REST GET {{{
+get_access_token();
+
+response_is(
+ url => "/=/model/User/id/$uid.yml",
+ code => 200,
+ method => 'GET',
+ testname => "200 - protected resource request",
+ consumer_secret => 'bar',
+ oauth_consumer_key => 'foo',
+ oauth_signature_method => 'PLAINTEXT',
+ oauth_token => $token_obj->token,
+ token_secret => $token_obj->secret,
+);
+$cmech->content_contains("You Zer", "REST GET works while OAuthed");
+# }}}
+# REST PUT {{{
+response_is(
+ url => "/=/model/User/id/$uid.yml",
+ code => 200,
+ method => 'DELETE',
+ testname => "200 - protected resource request",
+ consumer_secret => 'bar',
+ oauth_consumer_key => 'foo',
+ oauth_signature_method => 'PLAINTEXT',
+ oauth_token => $token_obj->token,
+ token_secret => $token_obj->secret,
+);
+$cmech->content_contains("You Zer", "REST DELETE doesn't work while the consumer has no write access");
+# }}}
+
More information about the Jifty-commit
mailing list