[Jifty-commit] r3672 - in apps/CASPlus/trunk: etc lib lib/CASPlus
lib/CASPlus/Model t
jifty-commit at lists.jifty.org
jifty-commit at lists.jifty.org
Wed Jul 11 18:29:30 EDT 2007
Author: sterling
Date: Wed Jul 11 18:29:29 2007
New Revision: 3672
Modified:
apps/CASPlus/trunk/ (props changed)
apps/CASPlus/trunk/etc/config.yml
apps/CASPlus/trunk/lib/CASPlus.pm
apps/CASPlus/trunk/lib/CASPlus/CurrentUser.pm
apps/CASPlus/trunk/lib/CASPlus/Model/ProfilePermission.pm
apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm
apps/CASPlus/trunk/lib/CASPlus/Model/UniqueProfileIdentifier.pm
apps/CASPlus/trunk/lib/CASPlus/ProfileBase.pm
apps/CASPlus/trunk/t/40-is_mine.t
Log:
r8032 at riddle: andrew | 2007-07-11 17:29:10 -0500
* Refactored is_mine in CASPlus::Model::ProfileBase to is_my in
CASPlus::CurrentUser.
* Added the parents_own_children and ownership_is_transitive properties to
relationship definitions.
* Fixed the unique IDs so that anyone can create and see them.
* Added the parents_of and children_of helpers to relationship definitions.
* I haven't completed the tests to this effect, but a profile objects other
than the one directly related to the current user may now be owned.
Modified: apps/CASPlus/trunk/etc/config.yml
==============================================================================
--- apps/CASPlus/trunk/etc/config.yml (original)
+++ apps/CASPlus/trunk/etc/config.yml Wed Jul 11 18:29:29 2007
@@ -65,3 +65,5 @@
TGC:
Length: 64
ExpirationTime: forever
+ Relationships:
+ MaximumOwnershipDepth: 5
Modified: apps/CASPlus/trunk/lib/CASPlus.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus.pm Wed Jul 11 18:29:29 2007
@@ -142,7 +142,7 @@
return 'allow';
}
- return undef;
+ return '';
},
);
@@ -154,7 +154,7 @@
return 'allow';
}
- return undef;
+ return '';
},
);
}
Modified: apps/CASPlus/trunk/lib/CASPlus/CurrentUser.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/CurrentUser.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/CurrentUser.pm Wed Jul 11 18:29:29 2007
@@ -64,7 +64,7 @@
=head2 profile_permissions
- my @permissions = $current_user->profile_ermissions;
+ my @permissions = $current_user->profile_permissions;
Returns a list of all the L<CASPlus::Model::ProfilePermission> objects associated with the current user.
@@ -102,6 +102,121 @@
return 0;
}
+=head2 profile
+
+ my $profile = $current_user->profile;
+
+This is a shortcut for:
+
+ my $profile = $current_user->id ? $current_user->user_object->profile
+ : undef;
+
+It retrieves the profile object that is linked to the current user.
+
+=cut
+
+sub profile {
+ my $self = shift;
+
+ return ($self->id ? $self->user_object->profile : undef);
+}
+
+=head2 is_my
+
+ my $mine = $current_user->is_my($object);
+
+Returns true if the profile object C<$object> is "owned" by the current user. This determines whether or not "my own" permissions are applicable for that object or not.
+
+As of this writing, an object is owned by the current user if any of the following are true:
+
+=over
+
+=item *
+
+The C<$object> is the profile for the current user. That is,
+
+ $current_user->profile->unique_id == $object->unique_id
+
+=item *
+
+The C<$object> is the direct descendant of C<< $current_user->profile >> via a relationship with L<CASPlus::Model::ProfileRelationship/parents_own_children>.
+
+=item *
+
+The C<$object> is the direct descendant of another profile object that is owned by C<< $current_user >> via a relationship with L<CASPlus::Model::ProfileRelationship/ownership_is_transitive>.
+
+=back
+
+=cut
+
+sub is_my {
+ my $self = shift;
+ my $object = shift;
+ my $object_id = $object->unique_id;
+
+ # Anonymous can't own anything, bummer.
+ return 0 if !$self->id;
+
+ # Get the profile belonging to the current user
+ my $profile = $self->profile;
+
+ # Check to see if the object is directly linked to the current user
+ return 1 if $profile->unique_id == $object_id;
+
+ # Load the relationships for the current profile
+ my $relationships = CASPlus::Model::ProfileRelationshipCollection->new;
+ $relationships->limit(
+ column => 'relation_parent',
+ value => $profile,
+ );
+ $relationships->limit(
+ column => 'parents_own_children',
+ value => 1,
+ );
+
+ # Iterate through the relationships and fill the queue
+ my @profile_queue;
+ while (my $relationship = $relationships->next) {
+ push @profile_queue,
+ map { [ $_, 1 ] } $relationship->children_of($object);
+ }
+
+ # Preload the maximum depth
+ my $maximum_depth
+ = Jifty->config->app('Relationships')->{'MaximumOwnershipDepth'};
+
+ # Process the queue
+ while (my $current = shift @profile_queue) {
+ my ($current_object, $current_depth) = @$current;
+
+ # Found it! It's mine!
+ return 1 if $current_object->unique_id = $object_id;
+
+ # At maximum depth, don't go deeper
+ next if $current_depth >= $maximum_depth;
+
+ # Find this profile's relationships
+ $relationships->unlimit;
+ $relationships->limit(
+ column => 'relation_parent',
+ value => $current_object->profile,
+ );
+ $relationships->limit(
+ column => 'ownership_is_transitive',
+ value => 1,
+ );
+
+ # Iterate through the relationships and fill the queue
+ while (my $relationship = $relationships->next) {
+ push @profile_queue,
+ map { [ $_, $current_depth + 1 ] }
+ $relationship->children_of($current_object);
+ }
+ }
+
+ return 0;
+}
+
=head1 AUTHOR
Andrew Sterling Hanenkamp, C<<hanenkamp at cpan.org>>>
Modified: apps/CASPlus/trunk/lib/CASPlus/Model/ProfilePermission.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/ProfilePermission.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/ProfilePermission.pm Wed Jul 11 18:29:29 2007
@@ -222,7 +222,7 @@
my $may_delete = $self->may_delete;
# if the object belongs to the current user, check the may my own flags
- if ($obj->is_mine) {
+ if (Jifty->web->current_user->is_my($obj)) {
$may_create ||= $self->may_create_my_own;
$may_read ||= $self->may_read_my_own;
$may_write ||= $self->may_write_my_own;
Modified: apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/ProfileRelationship.pm Wed Jul 11 18:29:29 2007
@@ -58,6 +58,20 @@
If this is set to true, any roles held by the parent object will also be passed to the children during role calculation.
+=head2 parents_own_children
+
+If this is set to a true value then this relationship confers ownership of the child to the parent object. This only makes sense when the parent profile is a has a C<profile_type> of "user". Only users can own things. And ownership of things other than self can only be granted by relationships with this set to true.
+
+This property doesn't imply any additional permissions unless the current user has applicable permissions through the C<may_*_my_own> properties.
+
+=head2 ownership_is_transitive
+
+If this is set to a true value then this relationship will make ownership transitive. That is, if the current user is considered the owner of X and X is the parent of Y via a relationship where C<ownership_is_transitive> is true, then the current user also owns Y.
+
+Be very careful about this property. Turning this on may result in an user account becoming "owner" without intending to do so.
+
+This property doesn't imply any additional permissions unless the current user has applicable permissions through the C<may_*_my_own> properties.
+
=head2 parent_column
Calculated column. Do not modify.
@@ -140,20 +154,6 @@
is immutable,
default is 0;
-# column count_parents =>
-# type is 'text',
-# label is 'From cardinality',
-# valid_values are qw/ 0..1 1 0..* 1..* /,
-# is mandatory,
-# default is '0..*';
-#
-# column count_children =>
-# type is 'text',
-# label is 'To cardinality',
-# valid_values are qw/ 0..1 1 0..* 1..* /,
-# is mandatory,
-# default is '1';
-
column dependent_relationship =>
type is 'boolean',
label is 'Dependent?',
@@ -167,6 +167,20 @@
hints is 'If checked, roles held by parent profiles are passed to the children.',
is mandatory,
default is 0;
+
+ column parents_own_children =>
+ type is 'boolean',
+ label is 'Parents own children?',
+ hints is 'The parent object has ownership privileges for the child if this is checked.',
+ is mandatory,
+ default is 0;
+
+ column ownership_is_transitive =>
+ type is 'boolean',
+ label is 'Ownership is transitive?',
+ hints is 'If parents own children and ownership is transitive, then the owner of the parent owns the children too.',
+ is mandatory,
+ default is 0;
column parent_column =>
refers_to Jifty::Model::ModelClassColumn,
@@ -268,7 +282,7 @@
name => $child_name,
refers_to_class => $link_table->qualified_class->collection_class,
refers_to_by => 'parent',
- storage_type => 'int',
+ virtual => 1,
);
# Create the relationship to the parents via the link table
@@ -278,7 +292,7 @@
name => $parent_name,
refers_to_class => $link_table->qualified_class->collection_class,
refers_to_by => 'child',
- storage_type => 'int',
+ virtual => 1,
);
# Associate all these tables and columns with the new object
@@ -302,6 +316,7 @@
name => $parent_name,
refers_to_class => $args->{relation_parent}->record_class->collection_class,
refers_to_by => $child_name,
+ virtual => 1,
);
}
@@ -311,6 +326,7 @@
model_class => $args->{relation_child}->model_class,
name => $parent_name,
refers_to_class => $args->{relation_parent}->record_class,
+ storage_type => 'int',
);
}
@@ -324,6 +340,7 @@
name => $child_name,
refers_to_class => $args->{relation_child}->record_class->collection_class,
refers_to_by => $parent_name,
+ virtual => 1,
);
}
@@ -333,6 +350,7 @@
model_class => $args->{relation_parent}->model_class,
name => $child_name,
refers_to_class => $args->{relation_child}->record_class,
+ storage_type => 'int',
);
}
@@ -354,6 +372,7 @@
name => $child_name,
refers_to_class => $args->{relation_child}->record_class,
refers_to_by => $parent_name,
+ storage_type => 'int',
);
}
@@ -457,6 +476,74 @@
return undef;
}
+=head2 children_of
+
+ my @children = $relationship->children_of($object);
+
+This is a helper method for code that needs to iterate over and apply a relationship to fetch all the children of the C<$object> according to this relationship. This will only work if C<$relationship> has L</relation_parent> set to the profile that describes C<$object>.
+
+=cut
+
+sub children_of {
+ my $self = shift;
+ my $object = shift;
+
+ my $method = $self->parent_column->name;
+ my @children;
+
+ # A many-to-many relationship, do the extra work of dereferencing links
+ if ($self->many_parents and $self->many_children) {
+ @children = map { $_->child } @{ $object->$method->items_array_ref };
+ }
+
+ # A one-to-many relationship
+ elsif ($self->many_children) {
+ @children = @{ $object->$method->items_array_ref };
+ }
+
+ # A one-to-one or many-to-one relationship
+ else {
+ my $child = $object->$method;
+ push @children, $child if $child->id;
+ }
+
+ return @children;
+}
+
+=item parents_of
+
+ my @parents = $relationship->parents_of($object)
+
+This is a helper that returns a list of all the parent of the C<$object> according to C<$relationship> without regard for the cardinality of either end of the relationship. For this to work, C<$relationship> must have the profile declaration for C<$object> as the L</relation_child>.
+
+=cut
+
+sub parents_of {
+ my $self = shift;
+ my $object = shift;
+
+ my $method = $self->child_column->name;
+ my @parents;
+
+ # A many-to-many relationship, do the extra work of dereferencing links
+ if ($self->many_parents and $self->many_children) {
+ @parents = map { $_->parent } @{ $object->$method->items_array_ref };
+ }
+
+ # A many-to-one relationship
+ elsif ($self->many_parents) {
+ @parents = @{ $object->$method->items_array_ref };
+ }
+
+ # A one-to-one or one-to-many relationship
+ else {
+ my $parent = $object->$method;
+ push @parents, $parent if $parent->id;
+ }
+
+ return @parents;
+}
+
=head1 AUTHOR
Andrew Sterling Hanenkamp, C<< <hanenkamp at cpan.org> >>
Modified: apps/CASPlus/trunk/lib/CASPlus/Model/UniqueProfileIdentifier.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/Model/UniqueProfileIdentifier.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/Model/UniqueProfileIdentifier.pm Wed Jul 11 18:29:29 2007
@@ -28,6 +28,25 @@
is immutable;
};
+=head1 METHODS
+
+=head2 current_user_can
+
+Allows all users to create and read unique IDs. Otherwise uses inherited permissions.
+
+=cut
+
+sub current_user_can {
+ my $self = shift;
+ my ($right) = @_;
+
+ if ($right eq 'read' or $right eq 'create') {
+ return 1;
+ }
+
+ return $self->SUPER::current_user_can(@_);
+}
+
=head2 WHY?
Every profile object is assigned a unique identifier primarily to aid in role caching.
Modified: apps/CASPlus/trunk/lib/CASPlus/ProfileBase.pm
==============================================================================
--- apps/CASPlus/trunk/lib/CASPlus/ProfileBase.pm (original)
+++ apps/CASPlus/trunk/lib/CASPlus/ProfileBase.pm Wed Jul 11 18:29:29 2007
@@ -18,86 +18,6 @@
=head1 METHODS
-=head2 is_mine
-
- my $mine = $object->is_mine;
-
-Returns true if the object is "owned" by the current owner.
-
-As of this writing, an object is owned by current user if any of the following are true:
-
-=over
-
-=item *
-
-The C<$object> is the profile for the current user.
-
-=item *
-
-The C<$object> is a child or descendant of the profile for the current user, as defined by the relationships (L<CASPlus::Model::ProfileRelationship>) the current user may traverse.
-
-=back
-
-=cut
-
-# XXX TODO FIXME Get _is_a_descendant working
-sub _is_a_descendant {
-# my $self = shift;
-# my $profile_obj = shift;
-# my $profile = shift;
-#
-# # Find all the relationships toward children of this object
-# my $relationships = CASPlus::Model::ProfileRelationshipCollection->new;
-# $relationships->limit(
-# column => 'relation_parent',
-# value => $profile,
-# );
-#
-# # For each, check to see if the object is a child or has a child
-# while (my $relationship = $relationships->next) {
-# if ($relationship->many_children) {
-# my $method = $relationship->child_column->name;
-# my $children = $profile_object->$method;
-#
-# while (my $child = $children->next) {
-# }
-# }
-# }
- return 0;
-}
-
-sub is_mine {
- my $self = shift;
- my $current_user = $self->current_user;
-
- # Do we have a real user?
- if ($current_user->id) {
-
- # Find out what kind of object I am
- my $profile = CASPlus::Model::Profile->new;
- $profile->load_by_profile_object($self);
-
- # Am I an object related to a user?
- if ($profile->profile_type eq 'user') {
-
- # I belong to the current user if I am directly related
- return $self->user_object->id
- && $self->user_object->id == $current_user->id;
- }
-
- # Otherwise, it might be something that's a child of my profile.
- # Descend all relationships to see if it is.
- else {
- return $self->_is_a_descendant($current_user->profile, $profile);
- }
- }
-
- # If I'm not a real user, it can't be mine
- else {
- return 0;
- }
-}
-
=head2 current_user_can
Any user with superuser user or the manage profile objects permission assigned to at least one role may access this object.
Modified: apps/CASPlus/trunk/t/40-is_mine.t
==============================================================================
--- apps/CASPlus/trunk/t/40-is_mine.t (original)
+++ apps/CASPlus/trunk/t/40-is_mine.t Wed Jul 11 18:29:29 2007
@@ -33,7 +33,8 @@
ok($employee->id, 'created an employee to test with');
# Set the user object as current
-Jifty->web->current_user($user);
+my $current_user = CASPlus::CurrentUser->new( id => $user->id );
+Jifty->web->current_user($current_user);
# Reload the employee
$employee = $employee_profile->record_class->new;
@@ -41,11 +42,11 @@
ok($employee->id, 'reloaded the test employee');
# It must be mine!
-ok($employee->is_mine, 'employee is_mine');
+ok($current_user->is_my($employee), 'is_my employee');
# Next phase: Create a child object, it also MUST BE MINE!!! Muahahahahaha
TODO: {
- local $TODO = 'is_mine is not yet defined for relationships';
+ local $TODO = 'is_my is not yet defined for relationships';
# Create a address profile
my $address_profile = CASPlus::Model::Profile->new(current_user => $system_user);
@@ -65,6 +66,7 @@
relation_child => $address_profile,
many_parents => 0,
many_children => 0,
+ parents_own_children => 1,
dependent_relationship => 1,
);
ok($relationship->id, 'Created an employee-address relationship');
@@ -81,5 +83,5 @@
$address = $address_profile->record_class->new;
$address->load($address_id);
ok($address->id, 'Reloaded the test address');
- ok($address->is_mine, 'The address is_mine!');
+ ok($current_user->is_my($address), 'it is_my address!');
};
More information about the Jifty-commit
mailing list