[Jifty-commit] r3241 - in jifty/trunk: lib/Jifty
t/TestApp/lib/TestApp/Model t/TestApp/t
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Sun May 13 22:02:24 EDT 2007
Author: sterling
Date: Sun May 13 22:02:23 2007
New Revision: 3241
Added:
jifty/trunk/t/TestApp/t/before_access.t
Modified:
jifty/trunk/ (props changed)
jifty/trunk/lib/Jifty/Record.pm
jifty/trunk/t/TestApp/lib/TestApp/Model/User.pm
jifty/trunk/t/TestApp/t/11-current_user.t
Log:
r5091 at dynpc145: andrew | 2007-05-13 21:00:23 -0500
Added the before_access trigger and a simple test for it. Also updated TestApp and the current_user test to make them compatible with the new before_access test.
Modified: jifty/trunk/lib/Jifty/Record.pm
==============================================================================
--- jifty/trunk/lib/Jifty/Record.pm (original)
+++ jifty/trunk/lib/Jifty/Record.pm Sun May 13 22:02:23 2007
@@ -178,19 +178,93 @@
Called before any attribute is changed on the object.
ATTRIBUTES is a hash of the arguments passed to _set.
-
-
=item delete
Called before the object is deleted.
=back
-The default implementation returns true if the current user is a
-superuser or a boostrap user. If the user is looking to delegate the
-access control decision to another object (by creating a
-C<delegate_current_user_can> subroutine), it hands off to that
-routine. Otherwise, it returns false.
+Models wishing to customize authorization checks should override this method. You you can do so like this:
+
+ sub current_user_can {
+ my ($self, $right, %args) = @_;
+
+ # Make any custom checks that return 1 to allow or return 0 to deny...
+
+ # Fallback upon the default implementation to handle the
+ # SkipAccessControl configuration setting, superuser, bootstrap,
+ # delegation, and the before_access hook
+ return $self->SUPER::current_user_can($right, %args);
+ }
+
+If you are sure you don't want your model to fallback using the default implementation, you can replace the last line with whatever fallback policy required.
+
+=head3 Authorization steps
+
+The default implementation proceeds as follows:
+
+=over
+
+=item 1.
+
+If the C<SkipAccessControl> setting is set to a true value in the framework configuration section of F<etc/config.yml>, C<current_user_can> always returns true.
+
+=item 2.
+
+The method first attempts to call the C<before_access> hooks to check for any
+allow or denial. See L</The before_access hook>.
+
+=item 3.
+
+Next, the default implementation returns true if the current user is a
+superuser or a boostrap user.
+
+=item 4.
+
+Then, if the model can perform delegation, usually by using
+L<Jifty::RightsFrom>, the access control decision is deferred to another object
+(via the C<delegate_current_user_can>
+subroutine).
+
+=item 5.
+
+Otherwise, it returns false.
+
+=back
+
+=head3 The before_access hook
+
+This implementation may make use of a trigger called C<before_access> to make the decision. A new handler can be added to the trigger point by calling C<add_handler>:
+
+ $record->add_trigger(
+ name => 'before_access',
+ code => \&before_access,
+ abortable => 1,
+ );
+
+The C<before_access> handler will be passed the same arguments that were used to call C<current_user_can>, including the current record object, the operation being checked, and any arguments being passed to the operation.
+
+The C<before_access> handler should return one of three strings: C<'deny'>, C<'allow'>, or C<'ignore'>. The C<current_user_can> implementation reacts as follows to these results:
+
+=over
+
+=item 1.
+
+If a handler is abortable and aborts by returning a false value (such as C<undef>), C<current_user_can> returns false.
+
+=item 2.
+
+If any handler returns 'deny', C<current_user_can> returns false.
+
+=item 3.
+
+If any handler returns 'allow' and no handler returns 'deny', C<current_user_can> returns true.
+
+=item 4.
+
+In all other cases, the results of the handlers are ignored and C<current_user_can> proceeds to check using superuser, bootstrap, and delegation.
+
+=back
=cut
@@ -198,10 +272,37 @@
my $self = shift;
my $right = shift;
+ # Turn off access control for the whole application
if (Jifty->config->framework('SkipAccessControl')) {
- return 1;
+ return 1;
+ }
+
+ my $hook_status = $self->call_trigger( before_access => $right, @_ );
+
+ # If not aborted...
+ if (defined $hook_status) {
+
+ # Compile the handler results
+ my %results;
+ $results{ $_->[0] }++ for (@{ $self->last_trigger_results });
+
+ # Deny always takes precedent
+ if ($results{deny}) {
+ return 0;
+ }
+
+ # Then allow...
+ elsif ($results{allow}) {
+ return 1;
+ }
+
+ # Otherwise, no instruction from the handlers, move along...
}
+ # Abort! Return false for safety
+ else {
+ return 0;
+ }
if ( $self->current_user->is_bootstrap_user
or $self->current_user->is_superuser )
Modified: jifty/trunk/t/TestApp/lib/TestApp/Model/User.pm
==============================================================================
--- jifty/trunk/t/TestApp/lib/TestApp/Model/User.pm (original)
+++ jifty/trunk/t/TestApp/lib/TestApp/Model/User.pm Sun May 13 22:02:23 2007
@@ -38,9 +38,6 @@
# Your model-specific methods go here.
-sub current_user_can {
- return 1;
-}
1;
Modified: jifty/trunk/t/TestApp/t/11-current_user.t
==============================================================================
--- jifty/trunk/t/TestApp/t/11-current_user.t (original)
+++ jifty/trunk/t/TestApp/t/11-current_user.t Sun May 13 22:02:23 2007
@@ -21,6 +21,9 @@
my $system_user = TestApp::CurrentUser->superuser;
ok($system_user, "Found a system user");
+# Make it so that all users have full access
+TestApp::Model::User->add_trigger( before_access => sub { 'allow' } );
+
# Create two users
my $o = TestApp::Model::User->new(current_user => $system_user);
$o->create( name => 'A User', email => 'auser at example.com',
Added: jifty/trunk/t/TestApp/t/before_access.t
==============================================================================
--- (empty file)
+++ jifty/trunk/t/TestApp/t/before_access.t Sun May 13 22:02:23 2007
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use lib 't/lib';
+use Jifty::SubTest;
+
+use Jifty::Test tests => 4;
+
+use_ok('TestApp::Model::User');
+
+# Create a superuser, which will be denied access in a bit
+my $system_user = TestApp::CurrentUser->superuser;
+ok($system_user, 'Found a system user');
+
+# Associate a new rule that only names starting with bob can be created
+TestApp::Model::User->add_trigger( before_access => sub {
+ my ($self, $right, %args) = @_;
+ return 'ignore' unless $right eq 'create';
+ unless ($args{name} =~ /^bob/) {
+ return 'deny';
+ }
+ return 'ignore';
+});
+
+# Try creating non-bob, which will be denied
+my $o = TestApp::Model::User->new(current_user => $system_user);
+my ($id) = $o->create( name => 'nonbob', email => $$, password => $$ );
+ok(!$id, 'User could not be created');
+
+# Try creating bobette, which will be allowd
+($id) = $o->create( name => 'bobette', email => $$, password => $$ );
+ok($id, 'User could be created');
More information about the Jifty-commit
mailing list