diff options
Diffstat (limited to 'rt/lib/RT/ACE.pm')
| -rwxr-xr-x | rt/lib/RT/ACE.pm | 806 | 
1 files changed, 638 insertions, 168 deletions
| diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm index 1501a125e..d4681cf44 100755 --- a/rt/lib/RT/ACE.pm +++ b/rt/lib/RT/ACE.pm @@ -1,304 +1,774 @@ -# BEGIN LICENSE BLOCK -#  -# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com> -#  -# (Except where explictly superceded by other copyright notices) -#  -# This work is made available to you under the terms of Version 2 of -# the GNU General Public License. A copy of that license should have -# been provided with this software, but in any event can be snarfed -# from www.gnu.org. -#  -# This work is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU -# General Public License for more details. -#  -# Unless otherwise specified, all modifications, corrections or -# extensions to this work which alter its source code become the -# property of Best Practical Solutions, LLC when submitted for -# inclusion in the work. -#  -#  -# END LICENSE BLOCK -# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>) -# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.   -#  -# !! DO NOT EDIT THIS FILE !! -# - -use strict; - +#$Header: /home/cvs/cvsroot/freeside/rt/lib/RT/ACE.pm,v 1.1 2002-08-12 06:17:07 ivan Exp $  =head1 NAME -RT::ACE - +  RT::ACE - RT\'s ACE object  =head1 SYNOPSIS +  use RT::ACE; +  my $ace = new RT::ACE($CurrentUser); + +  =head1 DESCRIPTION +  =head1 METHODS +=begin testing + +ok(require RT::TestHarness); +ok(require RT::ACE); + +=end testing +  =cut  package RT::ACE; -use RT::Record;  +use RT::Record; +@ISA= qw(RT::Record); +use vars qw (%SCOPES +   	     %QUEUERIGHTS +	     %SYSTEMRIGHTS +	     %LOWERCASERIGHTNAMES +	    );  -use vars qw( @ISA ); -@ISA= qw( RT::Record ); +%SCOPES = ( +	   System => 'System-level right', +	   Queue => 'Queue-level right' +	  ); -sub _Init { -  my $self = shift;  +# {{{ Descriptions of rights + +# Queue rights are the sort of queue rights that can only be granted +# to real people or groups +%QUEUERIGHTS = (  +		SeeQueue => 'Can this principal see this queue', +		AdminQueue => 'Create, delete and modify queues',  +		ShowACL => 'Display Access Control List', +		ModifyACL => 'Modify Access Control List', +		ModifyQueueWatchers => 'Modify the queue watchers', +                AdminKeywordSelects => 'Create, delete and modify keyword selections', + +		 +		ModifyTemplate => 'Modify email templates for this queue', +		ShowTemplate => 'Display email templates for this queue', +		ModifyScrips => 'Modify Scrips for this queue', +		ShowScrips => 'Display Scrips for this queue', + +		ShowTicket => 'Show ticket summaries', +		ShowTicketComments => 'Show ticket private commentary', + +		Watch => 'Sign up as a ticket Requestor or ticket or queue Cc', +		WatchAsAdminCc => 'Sign up as a ticket or queue AdminCc', +		CreateTicket => 'Create tickets in this queue', +		ReplyToTicket => 'Reply to tickets', +		CommentOnTicket => 'Comment on tickets', +		OwnTicket => 'Own tickets', +		ModifyTicket => 'Modify tickets', +		DeleteTicket => 'Delete tickets' + +	       );	 -  $self->Table('ACL'); -  $self->SUPER::_Init(@_); -} +# System rights are rights granted to the whole system +%SYSTEMRIGHTS = ( +                SuperUser => 'Do anything and everything', +		AdminKeywords => 'Creatte, delete and modify keywords',	  +		AdminGroups => 'Create, delete and modify groups', +	        AdminUsers => 'Create, Delete and Modify users', +		ModifySelf => 'Modify one\'s own RT account', +		); +# }}} +# {{{ Descriptions of principals -=item Create PARAMHASH +%TICKET_METAPRINCIPALS = ( Owner => 'The owner of a ticket', +            			   Requestor => 'The requestor of a ticket', +		            	   Cc => 'The CC of a ticket', +			               AdminCc => 'The administrative CC of a ticket', +			 ); -Create takes a hash of values and creates a row in the database: +# }}} -  varchar(25) 'PrincipalType'. -  int(11) 'PrincipalId'. -  varchar(25) 'RightName'. -  varchar(25) 'ObjectType'. -  int(11) 'ObjectId'. -  int(11) 'DelegatedBy'. -  int(11) 'DelegatedFrom'. +# {{{ We need to build a hash of all rights, keyed by lower case names + +#since you can't do case insensitive hash lookups + +foreach $right (keys %QUEUERIGHTS) { +    $LOWERCASERIGHTNAMES{lc $right}=$right; +} +foreach $right (keys %SYSTEMRIGHTS) { +    $LOWERCASERIGHTNAMES{lc $right}=$right; +} + +# }}} + +# {{{ sub _Init +sub _Init  { +  my $self = shift; +  $self->{'table'} = "ACL"; +  return($self->SUPER::_Init(@_)); +} +# }}} + +# {{{ sub LoadByValues + +=head2 LoadByValues PARAMHASH + +Load an ACE by specifying a paramhash with the following fields: + +              PrincipalId => undef, +	      PrincipalType => undef, +	      RightName => undef, +	      RightScope => undef, +	      RightAppliesTo => undef,  =cut +sub LoadByValues { +  my $self = shift; +  my %args = (PrincipalId => undef, +	      PrincipalType => undef, +	      RightName => undef, +	      RightScope => undef, +	      RightAppliesTo => undef, +	      @_); +   +  $self->LoadByCols (PrincipalId => $args{'PrincipalId'}, +		     PrincipalType => $args{'PrincipalType'}, +		     RightName => $args{'RightName'}, +		     RightScope => $args{'RightScope'}, +		     RightAppliesTo => $args{'RightAppliesTo'} +		    ); +   +  #If we couldn't load it. +  unless ($self->Id) { +      return (0, "ACE not found"); +  } +  # if we could +  return ($self->Id, "ACE Loaded"); +   +} + +# }}} +# {{{ sub Create +=head2 Create <PARAMS> + +PARAMS is a parameter hash with the following elements: + +   PrincipalType => "Queue"|"User" +   PrincipalId => an intentifier you can use to ->Load a user or group +   RightName => the name of a right. in any case +   RightScope => "System" | "Queue" +   RightAppliesTo => a queue id or undef + +=cut  sub Create {      my $self = shift; -    my %args = (  -                PrincipalType => '', -                PrincipalId => '0', -                RightName => '', -                ObjectType => '', -                ObjectId => '0', -                DelegatedBy => '0', -                DelegatedFrom => '0', - -		  @_); -    $self->SUPER::Create( -                         PrincipalType => $args{'PrincipalType'}, -                         PrincipalId => $args{'PrincipalId'}, -                         RightName => $args{'RightName'}, -                         ObjectType => $args{'ObjectType'}, -                         ObjectId => $args{'ObjectId'}, -                         DelegatedBy => $args{'DelegatedBy'}, -                         DelegatedFrom => $args{'DelegatedFrom'}, -); - +    my %args = ( PrincipalId => undef, +		 PrincipalType => undef, +		 RightName => undef, +		 RightScope => undef, +		 RightAppliesTo => undef, +		 @_ +	       ); +     +    # {{{ Validate the principal +    my ($princ_obj); +    if ($args{'PrincipalType'} eq 'User') { +	$princ_obj = new RT::User($RT::SystemUser); +	 +    }	 +    elsif ($args{'PrincipalType'} eq 'Group') { +	require RT::Group; +	$princ_obj = new RT::Group($RT::SystemUser); +    } +    else { +	return (0, 'Principal type '.$args{'PrincipalType'} . ' is invalid.'); +    }	 +     +    $princ_obj->Load($args{'PrincipalId'}); +    my $princ_id = $princ_obj->Id(); +     +    unless ($princ_id) { +	return (0, 'Principal '.$args{'PrincipalId'}.' not found.'); +    } + +    # }}} +     +    #TODO allow loading of queues by name.     +     +    # {{{ Check the ACL +    if ($args{'RightScope'} eq 'System') { +	 +	unless ($self->CurrentUserHasSystemRight('ModifyACL')) { +	    $RT::Logger->error("Permission Denied."); +	    return(undef); +	} +    } +     +    elsif ($args{'RightScope'} eq 'Queue') { +	unless ($self->CurrentUserHasQueueRight( Queue => $args{'RightAppliesTo'}, +						 Right => 'ModifyACL')) { +	    return (0, 'Permission Denied.'); +	} +	 +	 +	 +	 +    } +    #If it's not a scope we recognise, something scary is happening. +    else { +	$RT::Logger->err("RT::ACE->Create got a scope it didn't recognize: ". +			 $args{'RightScope'}." Bailing. \n"); +	return(0,"System error. Unable to grant rights."); +    } + +    # }}} + +    # {{{ Canonicalize and check the right name +    $args{'RightName'} = $self->CanonicalizeRightName($args{'RightName'}); +     +    #check if it's a valid RightName +    if ($args{'RightScope'} eq 'Queue') { +	unless (exists $QUEUERIGHTS{$args{'RightName'}}) { +	    return(0, 'Invalid right'); +	}	 +	}	 +    elsif ($args{'RightScope' eq 'System'}) { +	unless (exists $SYSTEMRIGHTS{$args{'RightName'}}) { +	    return(0, 'Invalid right'); +	}		     +    }	 +    # }}} +     +    # Make sure the right doesn't already exist. +    $self->LoadByCols (PrincipalId => $princ_id, +		       PrincipalType => $args{'PrincipalType'}, +		       RightName => $args{'RightName'}, +		       RightScope => $args {'RightScope'}, +		       RightAppliesTo => $args{'RightAppliesTo'} +		      ); +    if ($self->Id) { +	return (0, 'That user already has that right'); +    }	 + +    my $id = $self->SUPER::Create( PrincipalId => $princ_id, +				   PrincipalType => $args{'PrincipalType'}, +				   RightName => $args{'RightName'}, +				   RightScope => $args {'RightScope'}, +				   RightAppliesTo => $args{'RightAppliesTo'} +				 ); +     +     +    if ($id > 0 ) { +	return ($id, 'Right Granted'); +    } +    else { +	$RT::Logger->err('System error. right not granted.'); +	return(0, 'System Error. right not granted'); +    }  } +# }}} -=item id +# {{{ sub Delete  -Returns the current value of id.  -(In the database, id is stored as int(11).) +=head2 Delete +Delete this object.  =cut +sub Delete { +    my $self = shift; +     +    unless ($self->CurrentUserHasRight('ModifyACL')) { +	return (0, 'Permission Denied'); +    }	 +     +     +    my ($val,$msg) = $self->SUPER::Delete(@_); +    if ($val) { +	return ($val, 'ACE Deleted'); +    }	 +    else { +	return (0, 'ACE could not be deleted'); +    } +} -=item PrincipalType +# }}} -Returns the current value of PrincipalType.  -(In the database, PrincipalType is stored as varchar(25).) +# {{{ sub _BootstrapRight  +=head2 _BootstrapRight +Grant a right with no error checking and no ACL. this is _only_ for  +installation. If you use this routine without jesse@fsck.com's explicit  +written approval, he will hunt you down and make you spend eternity +translating mozilla's code into FORTRAN or intercal. -=item SetPrincipalType VALUE +=cut + +sub _BootstrapRight { +    my $self = shift; +    my %args = @_; + +    my $id = $self->SUPER::Create( PrincipalId => $args{'PrincipalId'}, +				   PrincipalType => $args{'PrincipalType'}, +				   RightName => $args{'RightName'}, +				   RightScope => $args {'RightScope'}, +				   RightAppliesTo => $args{'RightAppliesTo'} +				 ); +     +    if ($id > 0 ) { +	return ($id); +    } +    else { +	$RT::Logger->err('System error. right not granted.'); +	return(undef); +    } +     +} + +# }}} +# {{{ sub CanonicalizeRightName -Set PrincipalType to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, PrincipalType will be stored as a varchar(25).) +=head2 CanonicalizeRightName <RIGHT> +Takes a queue or system right name in any case and returns it in +the correct case. If it's not found, will return undef.  =cut +sub CanonicalizeRightName { +    my $self = shift; +    my $right = shift; +    $right = lc $right; +    if (exists $LOWERCASERIGHTNAMES{"$right"}) { +	return ($LOWERCASERIGHTNAMES{"$right"}); +    } +    else { +	return (undef); +    } +} + +# }}} -=item PrincipalId +# {{{ sub QueueRights -Returns the current value of PrincipalId.  -(In the database, PrincipalId is stored as int(11).) +=head2 QueueRights +Returns a hash of all the possible rights at the queue scope +=cut -=item SetPrincipalId VALUE +sub QueueRights { +        return (%QUEUERIGHTS); +} +# }}} -Set PrincipalId to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, PrincipalId will be stored as a int(11).) +# {{{ sub SystemRights +=head2 SystemRights + +Returns a hash of all the possible rights at the system scope  =cut +sub SystemRights { +	return (%SYSTEMRIGHTS); +} -=item RightName -Returns the current value of RightName.  -(In the database, RightName is stored as varchar(25).) +# }}} +# {{{ sub _Accessible  +sub _Accessible  { +  my $self = shift;   +  my %Cols = ( +	      PrincipalId => 'read/write', +	      PrincipalType => 'read/write', +	      RightName => 'read/write',  +	      RightScope => 'read/write', +	      RightAppliesTo => 'read/write' +	    ); +  return($self->SUPER::_Accessible(@_, %Cols)); +} +# }}} -=item SetRightName VALUE +# {{{ sub AppliesToObj +=head2 AppliesToObj -Set RightName to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, RightName will be stored as a varchar(25).) +If the AppliesTo is a queue, returns the queue object. If it's  +the system object, returns undef. If the user has no rights, returns undef. +=cut + +sub AppliesToObj { +    my $self = shift; +    if ($self->RightScope eq 'Queue') { +	my $appliesto_obj = new RT::Queue($self->CurrentUser); +	$appliesto_obj->Load($self->RightAppliesTo); +	return($appliesto_obj); +    } +    elsif ($self->RightScope eq 'System') { +	return (undef); +    }	 +    else { +	$RT::Logger->warning("$self -> AppliesToObj called for an object ". +			     "of an unknown scope:" . $self->RightScope); +	return(undef); +    } +}	 + +# }}} + +# {{{ sub PrincipalObj + +=head2 PrincipalObj + +If the AppliesTo is a group, returns the group object. +If the AppliesTo is a user, returns the user object. +Otherwise, it logs a warning and returns undef.  =cut +sub PrincipalObj { +    my $self = shift; +    my ($princ_obj); + +    if ($self->PrincipalType eq 'Group') { +	use RT::Group; +	$princ_obj = new RT::Group($self->CurrentUser); +    } +    elsif ($self->PrincipalType eq 'User') { +	$princ_obj = new RT::User($self->CurrentUser); +    } +    else { +	$RT::Logger->warning("$self -> PrincipalObj called for an object ". +			     "of an unknown principal type:" .  +			     $self->PrincipalType ."\n"); +	return(undef); +    } +     +    $princ_obj->Load($self->PrincipalId); +    return($princ_obj); + +}	 + +# }}} + +# {{{ ACL related methods + +# {{{ sub _Set + +sub _Set { +  my $self = shift; +  return (0, "ACEs can only be created and deleted."); +} -=item ObjectType +# }}} -Returns the current value of ObjectType.  -(In the database, ObjectType is stored as varchar(25).) +# {{{ sub _Value +sub _Value { +    my $self = shift; +    unless ($self->CurrentUserHasRight('ShowACL')) { +	return (undef); +    } -=item SetObjectType VALUE +    return ($self->__Value(@_)); +} + +# }}} -Set ObjectType to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, ObjectType will be stored as a varchar(25).) +# {{{ sub CurrentUserHasQueueRight  +=head2 CurrentUserHasQueueRight ( Queue => QUEUEID, Right => RIGHTNANAME ) + +Check to see whether the current user has the specified right for the specified queue.  =cut +sub CurrentUserHasQueueRight { +    my $self = shift; +    my %args = (Queue => undef, +		Right => undef, +		@_ +		); +    return ($self->HasRight( Right => $args{'Right'}, +			     Principal => $self->CurrentUser->UserObj, +			     Queue => $args{'Queue'})); +} + +# }}} + +# {{{ sub CurrentUserHasSystemRight  +=head2 CurrentUserHasSystemRight RIGHTNAME + +Check to see whether the current user has the specified right for the 'system' scope. + +=cut + +sub CurrentUserHasSystemRight { +    my $self = shift; +    my $right = shift; +    return ($self->HasRight( Right => $right, +			     Principal => $self->CurrentUser->UserObj, +			     System => 1 +			   )); +} + -=item ObjectId +# }}} -Returns the current value of ObjectId.  -(In the database, ObjectId is stored as int(11).) +# {{{ sub CurrentUserHasRight +=item CurrentUserHasRight RIGHT  +Takes a rightname as a string. +Helper menthod for HasRight. Presets Principal to CurrentUser then  +calls HasRight. -=item SetObjectId VALUE +=cut +sub CurrentUserHasRight { +    my $self = shift; +    my $right = shift; +    return ($self->HasRight( Principal => $self->CurrentUser->UserObj, +                             Right => $right, +			   )); +} -Set ObjectId to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, ObjectId will be stored as a int(11).) +# }}} +# {{{ sub HasRight + +=item HasRight + +Takes a param-hash consisting of "Right" and "Principal"  Principal is  +an RT::User object or an RT::CurrentUser object. "Right" is a textual +Right string that applies to KeywordSelects  =cut +sub HasRight { +    my $self = shift; +    my %args = ( Right => undef, +                 Principal => undef, +		 Queue => undef, +		 System => undef, +                 @_ );  + +    #If we're explicitly specifying a queue, as we need to do on create +    if (defined $args{'Queue'}) { +	return ($args{'Principal'}->HasQueueRight(Right => $args{'Right'}, +						  Queue => $args{'Queue'})); +    } +    #else if we're specifying to check a system right +    elsif ((defined $args{'System'}) and (defined $args{'Right'})) { +        return( $args{'Principal'}->HasSystemRight( $args{'Right'} )); +    }	 +     +    elsif ($self->__Value('RightScope') eq 'System') { +	return $args{'Principal'}->HasSystemRight($args{'Right'}); +    } +    elsif ($self->__Value('RightScope') eq 'Queue') { +	return $args{'Principal'}->HasQueueRight( Queue => $self->__Value('RightAppliesTo'), +						  Right => $args{'Right'} ); +    }	 +    else { +	$RT::Logger->warning("$self: Trying to check an acl for a scope we ". +			     "don't understand:" . $self->__Value('RightScope') ."\n"); +	return undef; +    } -=item DelegatedBy -Returns the current value of DelegatedBy.  -(In the database, DelegatedBy is stored as int(11).) +} +# }}} +# }}} -=item SetDelegatedBy VALUE +1; +__DATA__ -Set DelegatedBy to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, DelegatedBy will be stored as a int(11).) +# {{{ POD +=head1 Out of date docs -=cut +=head2 Table Structure +PrincipalType, PrincipalId, Right,Scope,AppliesTo -=item DelegatedFrom +=head1 The docs are out of date. so you know. -Returns the current value of DelegatedFrom.  -(In the database, DelegatedFrom is stored as int(11).) +=head1 Scopes +Scope is the scope of the right granted, not the granularity of the grant. +For example, Queue and Ticket rights are both granted for a "queue."  +Rights with a scope of 'System' don't have an AppliesTo. (They're global). +Rights with a scope of "Queue" are rights that act on a queue. +Rights with a scope of "System" are rights that act on some other aspect +of the system. -=item SetDelegatedFrom VALUE +=item Queue +=item System -Set DelegatedFrom to VALUE.  -Returns (1, 'Status message') on success and (0, 'Error Message') on failure. -(In the database, DelegatedFrom will be stored as a int(11).) +=head1 Rights +=head2 Scope: Queue -=cut +=head2 Queue rights that apply to a ticket within a queue +Create Ticket in <queue> +        Name: Create +	Principals: <user> <group> +Display Ticket Summary in <queue> -sub _ClassAccessible { -    { -      -        id => -		{read => 1, type => 'int(11)', default => ''}, -        PrincipalType =>  -		{read => 1, write => 1, type => 'varchar(25)', default => ''}, -        PrincipalId =>  -		{read => 1, write => 1, type => 'int(11)', default => '0'}, -        RightName =>  -		{read => 1, write => 1, type => 'varchar(25)', default => ''}, -        ObjectType =>  -		{read => 1, write => 1, type => 'varchar(25)', default => ''}, -        ObjectId =>  -		{read => 1, write => 1, type => 'int(11)', default => '0'}, -        DelegatedBy =>  -		{read => 1, write => 1, type => 'int(11)', default => '0'}, -        DelegatedFrom =>  -		{read => 1, write => 1, type => 'int(11)', default => '0'}, +	Name: Show +	Principals: <user> <group> Owner Requestor Cc AdminCc - } -}; +Display Ticket History  <queue> +	Name: ShowHistory +	Principals: <user> <group> Owner Requestor Cc AdminCc -        eval "require RT::ACE_Overlay"; -        if ($@ && $@ !~ qr{^Can't locate RT/ACE_Overlay.pm}) { -            die $@; -        }; +Display Ticket Private Comments  <queue> -        eval "require RT::ACE_Vendor"; -        if ($@ && $@ !~ qr{^Can't locate RT/ACE_Vendor.pm}) { -            die $@; -        }; +	Name: ShowComments +	Principals: <user> <group> Owner Requestor Cc AdminCc -        eval "require RT::ACE_Local"; -        if ($@ && $@ !~ qr{^Can't locate RT/ACE_Local.pm}) { -            die $@; -        }; +Reply to Ticket in <queue> +	Name: Reply +	Principals: <user> <group> Owner Requestor Cc AdminCc +Comment on Ticket in <queue> +	Name: Comment +	Principals: <user> <group> Owner Requestor Cc AdminCc -=head1 SEE ALSO +Modify Ticket in <queue> -This class allows "overlay" methods to be placed -into the following files _Overlay is for a System overlay by the original author, -_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.   +	Name: Modify +	Principals: <user> <group> Owner Requestor Cc AdminCc -These overlay files can contain new subs or subs to replace existing subs in this module. +Delete Tickets in <queue> -If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line  +	Name: Delete +	Principals: <user> <group> Owner Requestor Cc AdminCc -   no warnings qw(redefine); -so that perl does not kick and scream when you redefine a subroutine or variable in your overlay. +=head2 Queue Rights that apply to a whole queue -RT::ACE_Overlay, RT::ACE_Vendor, RT::ACE_Local +These rights can only be granted to "real people" -=cut +List Tickets in <queue> +	Name: ListQueue +	Principals: <user> <group> -1; +Know that <queue> exists +     +    Name: See +    Principals: <user> <group> + +Display queue settings + +    Name: Explore +    Principals: <user> <group> + +Modify Queue Watchers for <queue> + +	Name: ModifyQueueWatchers +	Principals: <user> <group> + +Modify Queue Attributes for <queue>  + +	Name: ModifyQueue +	Principals: <user> <group> + +Modify Queue ACL for queue <queue> + +	Name: ModifyACL +	Principals: <user> <group> + + +=head2 Rights that apply to the System scope + +=head2 SystemRights + +Create Queue +   +        Name: CreateQueue +	Principals: <user> <group> +Delete Queue +   +        Name: DeleteQueue +  	Principals: <user> <group> + +Create Users +   +        Name: CreateUser +	Principals: <user> <group> + +Delete Users +   +        Name: DeleteUser +  	Principals: <user> <group> +   +Modify Users +   +        Name: ModifyUser +	Principals: <user> <group> + +Modify Self +        Name: ModifySelf +	Principals: <user> <group> + +Browse Users + +        Name: BrowseUsers (NOT IMPLEMENTED in 2.0) +	Principals: <user> <group> + +Modify Self +		     +	Name: ModifySelf +	Principals: <user> <group> + +Modify System ACL + +	Name: ModifyACL		   +	Principals: <user> <group> + +=head1 The Principal Side of the ACE + +=head2 PrincipalTypes,PrincipalIds in our Neighborhood + +  User,<userid> +  Group,<groupip> +  Everyone,NULL + +=cut + +# }}} | 
