[Rt-commit] rt branch, 4.2/rights-api, created. rt-4.1.8-393-g672cafb

Alex Vandiver alexmv at bestpractical.com
Fri May 24 15:06:51 EDT 2013


The branch, 4.2/rights-api has been created
        at  672cafb30917b937319422fce350828c14f82887 (commit)

- Log -----------------------------------------------------------------
commit 2348440f769204e9cb7d70f1388b9e9cdd5b1092
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Thu May 9 01:39:49 2013 -0400

    Ensure that system rights do not move to "Status changes" category
    
    RegisterRights checks that a right name is not a pre-existing queue
    right before adding it as a new "Status" right.  However, if a right
    which is a system right and not a queue right (such as SuperUser) is
    used to limit a transition, the category and description are overidden.
    
    When a right name is given, ensure that it is neither a queue right nor
    a system right before overriding its category and description.

diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 2c30a4a..7ef435c 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -475,7 +475,8 @@ sub RegisterRights {
     my $RIGHTS = $RT::Queue::RIGHTS;
 
     while ( my ($right, $description) = each %rights ) {
-        next if exists $RIGHTS->{ $right };
+        next if exists $RIGHTS->{ $right }
+            or $RT::System::RIGHTS->{ $right };
 
         $RIGHTS->{ $right } = $description;
         RT::Queue->AddRightCategories( $right => 'Status' );

commit bab8ef2e81ede6a51eba564a9e09831e2ed37928
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Tue May 21 04:07:00 2013 -0400

    Make right regiration be specific to the lifecycle type
    
    Lifecycles on non-tickets should not add rights which are scoped to
    queues.  As such, defer right registration to the subclass, and ensure
    that it gets run once per type by moving it to running at FillCache
    time.

diff --git a/lib/RT/Lifecycle.pm b/lib/RT/Lifecycle.pm
index 7ef435c..a31c394 100644
--- a/lib/RT/Lifecycle.pm
+++ b/lib/RT/Lifecycle.pm
@@ -55,7 +55,6 @@ package RT::Lifecycle;
 our %LIFECYCLES;
 our %LIFECYCLES_CACHE;
 our %LIFECYCLES_TYPES;
-__PACKAGE__->RegisterRights;
 
 # cache structure:
 #    {
@@ -457,34 +456,7 @@ sub CheckRight {
     return $to eq 'deleted' ? 'DeleteTicket' : 'ModifyTicket';
 }
 
-=head3 RegisterRights
-
-Registers all defined rights in the system, so they can be addigned
-to users. No need to call it, as it's called when module is loaded.
-
-=cut
-
-sub RegisterRights {
-    my $self = shift;
-
-    my %rights = $self->RightsDescription;
-
-    require RT::ACE;
-
-    require RT::Queue;
-    my $RIGHTS = $RT::Queue::RIGHTS;
-
-    while ( my ($right, $description) = each %rights ) {
-        next if exists $RIGHTS->{ $right }
-            or $RT::System::RIGHTS->{ $right };
-
-        $RIGHTS->{ $right } = $description;
-        RT::Queue->AddRightCategories( $right => 'Status' );
-        $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
-    }
-}
-
-=head3 RightsDescription
+=head3 RightsDescription [TYPE]
 
 Returns hash with description of rights that are defined for
 particular transitions.
@@ -493,12 +465,14 @@ particular transitions.
 
 sub RightsDescription {
     my $self = shift;
+    my $type = shift;
 
     $self->FillCache unless keys %LIFECYCLES_CACHE;
 
     my %tmp;
     foreach my $lifecycle ( values %LIFECYCLES_CACHE ) {
         next unless exists $lifecycle->{'rights'};
+        next if $type and $lifecycle->{type} ne $type;
         while ( my ($transition, $right) = each %{ $lifecycle->{'rights'} } ) {
             push @{ $tmp{ $right } ||=[] }, $transition;
         }
@@ -681,6 +655,10 @@ sub FillCache {
             push @{ $LIFECYCLES_TYPES{$type}{''} },
                 @{ $LIFECYCLES_TYPES{$type}{$category} } if $category;
         }
+
+        my $class = "RT::Lifecycle::".ucfirst($type);
+        $class->RegisterRights if $class->require
+            and $class->can("RegisterRights");
     }
 
     foreach my $lifecycle ( values %LIFECYCLES_CACHE ) {
diff --git a/lib/RT/Lifecycle/Ticket.pm b/lib/RT/Lifecycle/Ticket.pm
index b864114..e8f56e8 100644
--- a/lib/RT/Lifecycle/Ticket.pm
+++ b/lib/RT/Lifecycle/Ticket.pm
@@ -101,4 +101,31 @@ sub ReminderStatusOnResolve {
     return $self->DefaultStatus('reminder_on_resolve') || 'resolved';
 }
 
+=head2 RegisterRights
+
+Ticket lifecycle rights are registered (and thus grantable) at the queue
+level.
+
+=cut
+
+sub RegisterRights {
+    my $self = shift;
+
+    my %rights = $self->RightsDescription( 'ticket' );
+
+    require RT::ACE;
+
+    require RT::Queue;
+    my $RIGHTS = $RT::Queue::RIGHTS;
+
+    while ( my ($right, $description) = each %rights ) {
+        next if exists $RIGHTS->{ $right }
+            or $RT::System::RIGHTS->{ $right };
+
+        $RIGHTS->{ $right } = $description;
+        RT::Queue->AddRightCategories( $right => 'Status' );
+        $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+    }
+}
+
 1;

commit 3d566af7d7f451e9bbf6c9cd308a5d17a0c5dc4c
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri May 24 14:52:30 2013 -0400

    Ensure that rights from lifecycles are loaded during RT->Init
    
    Previous to bab8ef2, rights from lifecycles were loaded when
    RT::Lifecycle was use'd or require'd -- which, as it happens, was only
    in RT->InitClasses.  This is fortunate, because had it been use'd
    anywhere, the Lifecycle configuration would have not yet been loaded,
    and thus no rights would have been assigned.
    
    bab8ef2 moves rights registration to cache-fill time, which is generally
    only upon access of statuses of an object.  This is to late for @ACL
    initialdata insersion, however.  Move this earlier, to during ->Init, so
    that lifecycle rights are realized early enough.

diff --git a/lib/RT.pm b/lib/RT.pm
index b43fdfd..368770a 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -197,7 +197,7 @@ sub Init {
     InitPlugins();
     RT::I18N->Init;
     RT->Config->PostLoadCheck;
-
+    RT::Lifecycle->new->FillCache;
 }
 
 =head2 ConnectToDatabase

commit d645bf558917c649506e526003c0abe960a9113f
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Tue May 21 22:04:22 2013 -0400

    Add a unified CurrentUserHasright and HasRight method
    
    Many classes cargo-cult similar code; push the common simple
    implementation into RT::Record, and remove all straightforward alternate
    definitions.

diff --git a/lib/RT/Article.pm b/lib/RT/Article.pm
index 19971a3..2f218c8 100644
--- a/lib/RT/Article.pm
+++ b/lib/RT/Article.pm
@@ -499,25 +499,6 @@ sub DeleteTopic {
     }
 }
 
-=head2 CurrentUserHasRight
-
-Returns true if the current user has the right for this article, for the whole system or for this article's class
-
-=cut
-
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-
-    return (
-        $self->CurrentUser->HasRight(
-            Right        => $right,
-            Object       => $self,
-        )
-    );
-
-}
-
 =head2 CurrentUserCanSee
 
 Returns true if the current user can see the article, using ShowArticle
diff --git a/lib/RT/Class.pm b/lib/RT/Class.pm
index 49153f4..af23c77 100644
--- a/lib/RT/Class.pm
+++ b/lib/RT/Class.pm
@@ -255,19 +255,6 @@ sub _Value {
 
 # }}}
 
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-
-    return (
-        $self->CurrentUser->HasRight(
-            Right        => $right,
-            Object       => ( $self->Id ? $self : RT->System ),
-        )
-    );
-
-}
-
 sub ArticleCustomFields {
     my $self = shift;
 
diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 3051de0..91c72ee 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -850,22 +850,6 @@ sub UnlimitedValues {
 }
 
 
-=head2 CurrentUserHasRight RIGHT
-
-Helper function to call the custom field's queue's CurrentUserHasRight with the passed in args.
-
-=cut
-
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-
-    return $self->CurrentUser->HasRight(
-        Object => $self,
-        Right  => $right,
-    );
-}
-
 =head2 ACLEquivalenceObjects
 
 Returns list of objects via which users can get rights on this custom field. For custom fields
diff --git a/lib/RT/Group.pm b/lib/RT/Group.pm
index 11f7e66..8c65230 100644
--- a/lib/RT/Group.pm
+++ b/lib/RT/Group.pm
@@ -1378,40 +1378,6 @@ sub _Set {
     }
 }
 
-
-
-
-
-=head2 CurrentUserHasRight RIGHTNAME
-
-Returns true if the current user has the specified right for this group.
-
-
-    TODO: we don't deal with membership visibility yet
-
-=cut
-
-
-sub CurrentUserHasRight {
-    my $self = shift;
-    my $right = shift;
-
-
-
-    if ($self->Id &&
-                $self->CurrentUser->HasRight( Object => $self,
-                                              Right => $right )) {
-        return(1);
-    }
-    elsif ( $self->CurrentUser->HasRight(Object => $RT::System, Right =>  $right )) {
-        return (1);
-    } else {
-        return(undef);
-    }
-
-}
-
-
 =head2 CurrentUserCanSee
 
 Always returns 1; unfortunately, for historical reasons, users have
diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
index e89e015..0f32ad2 100644
--- a/lib/RT/Queue.pm
+++ b/lib/RT/Queue.pm
@@ -905,29 +905,6 @@ sub _Value {
     return ( $self->__Value(@_) );
 }
 
-
-
-=head2 CurrentUserHasRight
-
-Takes one argument. A textual string with the name of the right we want to check.
-Returns true if the current user has that right for this queue.
-Returns undef otherwise.
-
-=cut
-
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-
-    return (
-        $self->HasRight(
-            Principal => $self->CurrentUser,
-            Right     => "$right"
-          )
-    );
-
-}
-
 =head2 CurrentUserCanSee
 
 Returns true if the current user can see the queue, using SeeQueue
@@ -940,39 +917,6 @@ sub CurrentUserCanSee {
     return $self->CurrentUserHasRight('SeeQueue');
 }
 
-
-=head2 HasRight
-
-Takes a param hash with the fields 'Right' and 'Principal'.
-Principal defaults to the current user.
-Returns true if the principal has that right for this queue.
-Returns undef otherwise.
-
-=cut
-
-# TAKES: Right and optional "Principal" which defaults to the current user
-sub HasRight {
-    my $self = shift;
-    my %args = (
-        Right     => undef,
-        Principal => $self->CurrentUser,
-        @_
-    );
-    my $principal = delete $args{'Principal'};
-    unless ( $principal ) {
-        $RT::Logger->error("Principal undefined in Queue::HasRight");
-        return undef;
-    }
-
-    return $principal->HasRight(
-        %args,
-        Object => ($self->Id ? $self : $RT::System),
-    );
-}
-
-
-
-
 =head2 id
 
 Returns the current value of id. 
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index df89a38..02f548e 100644
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -2226,6 +2226,37 @@ sub LoadCustomFieldByIdentifier {
 
 sub ACLEquivalenceObjects { } 
 
+=head2 HasRight
+
+ Takes a paramhash with the attributes 'Right' and 'Principal'
+  'Right' is a ticket-scoped textual right from RT::ACE 
+  'Principal' is an RT::User object
+
+  Returns 1 if the principal has the right. Returns undef if not.
+
+=cut
+
+sub HasRight {
+    my $self = shift;
+    my %args = (
+        Right     => undef,
+        Principal => undef,
+        @_
+    );
+
+    $args{Principal} ||= $self->CurrentUser->PrincipalObj;
+
+    return $args{'Principal'}->HasRight(
+        Object => $self->Id ? $self : $RT::System,
+        Right  => $args{'Right'}
+    );
+}
+
+sub CurrentUserHasRight {
+    my $self = shift;
+    return $self->HasRight( Right => @_ );
+}
+
 sub ModifyLinkRight { }
 
 =head2 ColumnMapClassName
diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 9525430..d77292c 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -669,25 +669,6 @@ sub _Value {
     return $self->__Value(@_);
 }
 
-
-
-=head2 CurrentUserHasRight
-
-Helper menthod for HasRight. Presets Principal to CurrentUser then 
-calls HasRight.
-
-=cut
-
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-    return ( $self->HasRight( Principal => $self->CurrentUser->UserObj,
-                              Right     => $right ) );
-
-}
-
-
-
 =head2 HasRight
 
 Takes a param-hash consisting of "Right" and "Principal"  Principal is 
@@ -702,6 +683,8 @@ sub HasRight {
                  Principal => undef,
                  @_ );
 
+    $args{Principal} ||= $self->CurrentUser->PrincipalObj;
+
     my $queues = $self->AddedTo;
     my $found = 0;
     while ( my $queue = $queues->Next ) {
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index e540403..f5b524c 100644
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -2864,28 +2864,6 @@ sub _UpdateTimeTaken {
     return ($Total);
 }
 
-
-
-
-
-=head2 CurrentUserHasRight
-
-  Takes the textual name of a Ticket scoped right (from RT::ACE) and returns
-1 if the user has that right. It returns 0 if the user doesn't have that right.
-
-=cut
-
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-
-    return $self->CurrentUser->PrincipalObj->HasRight(
-        Object => $self,
-        Right  => $right,
-    )
-}
-
-
 =head2 CurrentUserCanSee
 
 Returns true if the current user can see the ticket, using ShowTicket
@@ -2897,41 +2875,6 @@ sub CurrentUserCanSee {
     return $self->CurrentUserHasRight('ShowTicket');
 }
 
-=head2 HasRight
-
- Takes a paramhash with the attributes 'Right' and 'Principal'
-  'Right' is a ticket-scoped textual right from RT::ACE 
-  'Principal' is an RT::User object
-
-  Returns 1 if the principal has the right. Returns undef if not.
-
-=cut
-
-sub HasRight {
-    my $self = shift;
-    my %args = (
-        Right     => undef,
-        Principal => undef,
-        @_
-    );
-
-    unless ( ( defined $args{'Principal'} ) and ( ref( $args{'Principal'} ) ) )
-    {
-        Carp::cluck("Principal attrib undefined for Ticket::HasRight");
-        $RT::Logger->crit("Principal attrib undefined for Ticket::HasRight");
-        return(undef);
-    }
-
-    return (
-        $args{'Principal'}->HasRight(
-            Object => $self,
-            Right     => $args{'Right'}
-          )
-    );
-}
-
-
-
 =head2 Reminders
 
 Return the Reminders object for this ticket. (It's an RT::Reminders object.)
diff --git a/lib/RT/Topic.pm b/lib/RT/Topic.pm
index b649266..bba8a42 100644
--- a/lib/RT/Topic.pm
+++ b/lib/RT/Topic.pm
@@ -212,18 +212,24 @@ sub _Set {
 # }}}
 
 
-# {{{ CurrentUserHasRight
+# {{{ HasRight
 
-=head2 CurrentUserHasRight
+=head2 HasRight Principal => [...], Right => [...]
 
-Returns true if the current user has the right for this topic, for the
-whole system or for whatever object this topic is associated with
+Returns true if the given principal has the right for this topic, for
+the whole system or for whatever object this topic is associated with
 
 =cut
 
-sub CurrentUserHasRight {
+sub HasRight {
     my $self  = shift;
-    my $right = shift;
+    my %args = (
+        Right     => undef,
+        Principal => undef,
+        @_,
+    );
+
+    $args{Principal} ||= $self->CurrentUser->PrincipalObj;
 
     my $equiv = [ $RT::System ];
     if ($self->ObjectId) {
@@ -233,18 +239,18 @@ sub CurrentUserHasRight {
     }
     if ($self->Id) {
         return ( $self->CurrentUser->HasRight(
-                                              Right        => $right,
-                                              Object       => $self,
-                                              EquivObjects => $equiv,
-                                             ) );
+            @_,
+            Object       => $self,
+            EquivObjects => $equiv,
+        ) );
     } else {
         # If we don't have an ID, we don't even know what object we're
         # attached to -- so the only thing we can fall back on is the
         # system object.
         return ( $self->CurrentUser->HasRight(
-                                              Right        => $right,
-                                              Object       => $RT::System,
-                                             ) );
+            @_,
+            Object       => $RT::System,
+        ) );
     }
     
 
diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
index 67e650c..b74f5d2 100644
--- a/lib/RT/Transaction.pm
+++ b/lib/RT/Transaction.pm
@@ -1132,23 +1132,6 @@ sub _Value {
 }
 
 
-
-=head2 CurrentUserHasRight RIGHT
-
-Calls $self->CurrentUser->HasQueueRight for the right passed in here.
-passed in here.
-
-=cut
-
-sub CurrentUserHasRight {
-    my $self  = shift;
-    my $right = shift;
-    return $self->CurrentUser->HasRight(
-        Right  => $right,
-        Object => $self->Object
-    );
-}
-
 =head2 CurrentUserCanSee
 
 Returns true if current user has rights to see this particular transaction.

commit a1e97e060e300177b3f666f7b4f5129d6e04a303
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Tue May 21 22:25:11 2013 -0400

    Topics' custom HasRight is easily reducable to a ACLEquivalenceObjects method
    
    RT::Principal->HasRight already provides a simple means to add
    additional EquivObject, by calling the ACLEquivalenceObjects method on
    the target object.  Rather than rolling a complete custom HasRight
    method, provide the equivalent ACLEquivalenceObjects method.

diff --git a/lib/RT/Topic.pm b/lib/RT/Topic.pm
index bba8a42..dfd900f 100644
--- a/lib/RT/Topic.pm
+++ b/lib/RT/Topic.pm
@@ -212,52 +212,21 @@ sub _Set {
 # }}}
 
 
-# {{{ HasRight
+=head2 ACLEquivalenceObjects
 
-=head2 HasRight Principal => [...], Right => [...]
-
-Returns true if the given principal has the right for this topic, for
-the whole system or for whatever object this topic is associated with
+Rights on the topic are inherited from the object it is a topic on.
 
 =cut
 
-sub HasRight {
+sub ACLEquivalenceObjects {
     my $self  = shift;
-    my %args = (
-        Right     => undef,
-        Principal => undef,
-        @_,
-    );
-
-    $args{Principal} ||= $self->CurrentUser->PrincipalObj;
-
-    my $equiv = [ $RT::System ];
-    if ($self->ObjectId) {
-        my $obj = $self->ObjectType->new($self->CurrentUser);
-        $obj->Load($self->ObjectId);
-        push @{$equiv}, $obj;
-    }
-    if ($self->Id) {
-        return ( $self->CurrentUser->HasRight(
-            @_,
-            Object       => $self,
-            EquivObjects => $equiv,
-        ) );
-    } else {
-        # If we don't have an ID, we don't even know what object we're
-        # attached to -- so the only thing we can fall back on is the
-        # system object.
-        return ( $self->CurrentUser->HasRight(
-            @_,
-            Object       => $RT::System,
-        ) );
-    }
-    
+    return unless $self->id and $self->ObjectId;
 
+    my $obj = $self->ObjectType->new($self->CurrentUser);
+    $obj->Load($self->ObjectId);
+    return $obj;
 }
 
-# }}}
-
 
 =head2 id
 

commit abf15b97a8d4bb7c3fe701a13881ac0eed0fb987
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Tue May 21 22:38:06 2013 -0400

    Rights on any of the of a scrip's applied objects is sufficient
    
    Rather than explicitly iterating and checking HasRight on each of the
    applied objects, one can check all such objects at once by providing a
    suitable ACLEquivalenceObjects method which includes all applied
    objects.  This also deals with the implicit RT::System check, as well.

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index d77292c..c231f44 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -669,36 +669,17 @@ sub _Value {
     return $self->__Value(@_);
 }
 
-=head2 HasRight
+=head2 ACLEquivalenceObjects
 
-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 Scrips.
+Having rights on any of the queues the scrip applies to is equivalent to
+having rights on the scrip.
 
 =cut
 
-sub HasRight {
+sub ACLEquivalenceObjects {
     my $self = shift;
-    my %args = ( Right     => undef,
-                 Principal => undef,
-                 @_ );
-
-    $args{Principal} ||= $self->CurrentUser->PrincipalObj;
-
-    my $queues = $self->AddedTo;
-    my $found = 0;
-    while ( my $queue = $queues->Next ) {
-        return 1 if $args{'Principal'}->HasRight(
-            Right  => $args{'Right'},
-            Object => $queue,
-        );
-        $found = 1;
-    }
-    return $args{'Principal'}->HasRight(
-        Object => $RT::System,
-        Right  => $args{'Right'},
-    ) unless $found;
-    return 0;
+    return unless $self->id;
+    return @{ $self->AddedTo->ItemsArrayRef };
 }
 
 

commit 672cafb30917b937319422fce350828c14f82887
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Wed May 22 01:44:13 2013 -0400

    Refactor rights management into a centralized store, and a role
    
    Rights management for existing classes was done ad-hoc, by
    re-implementing the same methods in every class which needed them -- a
    pattern ripe for refactoring into a role.  At the same time, the
    information was centralized into a single %RT::ACL::RIGHTS datastore,
    rather than being stored in multiple inconsistent class variables, and
    the API for adding new rights was considerably streamlined.

diff --git a/lib/RT/ACE.pm b/lib/RT/ACE.pm
index e3350e5..f9879d7 100644
--- a/lib/RT/ACE.pm
+++ b/lib/RT/ACE.pm
@@ -75,10 +75,7 @@ require RT::Principals;
 require RT::Queues;
 require RT::Groups;
 
-use vars qw (
-  %LOWERCASERIGHTNAMES
-  %OBJECT_TYPES
-);
+our %RIGHTS;
 
 my (@_ACL_CACHE_HANDLERS);
 
@@ -468,13 +465,17 @@ the correct case. If it's not found, will return undef.
 =cut
 
 sub CanonicalizeRightName {
-    my $self  = shift;
-    return $LOWERCASERIGHTNAMES{ lc shift };
+    my $self = shift;
+    my $name = shift;
+    for my $class (sort keys %RIGHTS) {
+        return $RIGHTS{$class}{ lc $name }{Name}
+            if $RIGHTS{$class}{ lc $name };
+    }
+    return undef;
 }
 
 
 
-
 =head2 Object
 
 If the object this ACE applies to is a queue, returns the queue object. 
@@ -493,7 +494,7 @@ sub Object {
 
     my $appliesto_obj;
 
-    if ($self->__Value('ObjectType') && $OBJECT_TYPES{$self->__Value('ObjectType')} ) {
+    if ($self->__Value('ObjectType') && $self->__Value('ObjectType')->DOES('RT::Record::Role::Rights') ) {
         $appliesto_obj =  $self->__Value('ObjectType')->new($self->CurrentUser);
         unless (ref( $appliesto_obj) eq $self->__Value('ObjectType')) {
             return undef;
diff --git a/lib/RT/Class.pm b/lib/RT/Class.pm
index af23c77..e94ff31 100644
--- a/lib/RT/Class.pm
+++ b/lib/RT/Class.pm
@@ -60,6 +60,9 @@ use RT::Articles;
 use RT::ObjectClass;
 use RT::ObjectClasses;
 
+use Role::Basic 'with';
+with "RT::Record::Role::Rights";
+
 sub Table {'Classes'}
 
 =head2 Load IDENTIFIER
@@ -81,94 +84,17 @@ sub Load {
     }
 }
 
-# {{{ This object provides ACLs
-
-use vars qw/$RIGHTS/;
-$RIGHTS = {
-    SeeClass            => 'See that this class exists',               #loc_pair
-    CreateArticle       => 'Create articles in this class',            #loc_pair
-    ShowArticle         => 'See articles in this class',               #loc_pair
-    ShowArticleHistory  => 'See changes to articles in this class',    #loc_pair
-    ModifyArticle       => 'Modify or delete articles in this class',  #loc_pair
-    ModifyArticleTopics => 'Modify topics for articles in this class', #loc_pair
-    AdminClass          => 'Modify metadata and custom fields for this class',              #loc_pair
-    AdminTopics         => 'Modify topic hierarchy associated with this class',             #loc_pair
-    ShowACL             => 'Display Access Control List',              #loc_pair
-    ModifyACL           => 'Create, modify and delete Access Control List entries',         #loc_pair
-    DeleteArticle       => 'Delete articles in this class',            #loc_pair
-};
-
-our $RIGHT_CATEGORIES = {
-    SeeClass            => 'Staff',
-    CreateArticle       => 'Staff',
-    ShowArticle         => 'General',
-    ShowArticleHistory  => 'Staff',
-    ModifyArticle       => 'Staff',
-    ModifyArticleTopics => 'Staff',
-    AdminClass          => 'Admin',
-    AdminTopics         => 'Admin',
-    ShowACL             => 'Admin',
-    ModifyACL           => 'Admin',
-    DeleteArticle       => 'Staff',
-};
-
-# TODO: This should be refactored out into an RT::ACLedObject or something
-# stuff the rights into a hash of rights that can exist.
-
-# Tell RT::ACE that this sort of object can get acls granted
-$RT::ACE::OBJECT_TYPES{'RT::Class'} = 1;
-
-# TODO this is ripe for a refacor, since this is stolen from Queue
-__PACKAGE__->AddRights(%$RIGHTS);
-__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
-
-=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
-
-Adds the given rights to the list of possible rights.  This method
-should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRights {
-    my $self = shift;
-    my %new = @_;
-    $RIGHTS = { %$RIGHTS, %new };
-    %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
-                                      map { lc($_) => $_ } keys %new);
-}
-
-=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
-
-Adds the given right and category pairs to the list of right categories.  This
-method should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRightCategories {
-    my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
-    my %new = @_;
-    $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
-}
-
-=head2 AvailableRights
-
-Returns a hash of available rights for this object. The keys are the right names and the values are a description of what t
-he rights do
-
-=cut
-
-sub AvailableRights {
-    my $self = shift;
-    return ($RIGHTS);
-}
-
-sub RightCategories {
-    return $RIGHT_CATEGORIES;
-}
-
-
-# }}}
-
+__PACKAGE__->AddRight( Staff   => SeeClass            => 'See that this class exists'); # loc_pair
+__PACKAGE__->AddRight( Staff   => CreateArticle       => 'Create articles in this class'); # loc_pair
+__PACKAGE__->AddRight( General => ShowArticle         => 'See articles in this class'); # loc_pair
+__PACKAGE__->AddRight( Staff   => ShowArticleHistory  => 'See changes to articles in this class'); # loc_pair
+__PACKAGE__->AddRight( Staff   => ModifyArticle       => 'Modify or delete articles in this class'); # loc_pair
+__PACKAGE__->AddRight( Staff   => ModifyArticleTopics => 'Modify topics for articles in this class'); # loc_pair
+__PACKAGE__->AddRight( Admin   => AdminClass          => 'Modify metadata and custom fields for this class'); # loc_pair
+__PACKAGE__->AddRight( Admin   => AdminTopics         => 'Modify topic hierarchy associated with this class'); # loc_pair
+__PACKAGE__->AddRight( Admin   => ShowACL             => 'Display Access Control List'); # loc_pair
+__PACKAGE__->AddRight( Admin   => ModifyACL           => 'Create, modify and delete Access Control List entries'); # loc_pair
+__PACKAGE__->AddRight( Staff   => DeleteArticle       => 'Delete articles in this class'); # loc_pair
 
 # {{{ Create
 
diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 91c72ee..3603819 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -55,6 +55,9 @@ use warnings;
 
 use base 'RT::Record';
 
+use Role::Basic 'with';
+with "RT::Record::Role::Rights";
+
 sub Table {'CustomFields'}
 
 use Scalar::Util qw(blessed);
@@ -207,69 +210,10 @@ __PACKAGE__->RegisterBuiltInGroupings(
     'RT::User'      => [ 'Identity', 'Access control', 'Location', 'Phones' ],
 );
 
-our $RIGHTS = {
-    SeeCustomField            => 'View custom fields',                                    # loc_pair
-    AdminCustomField          => 'Create, modify and delete custom fields',               # loc_pair
-    AdminCustomFieldValues    => 'Create, modify and delete custom fields values',        # loc_pair
-    ModifyCustomField         => 'Add, modify and delete custom field values for objects' # loc_pair
-};
-
-our $RIGHT_CATEGORIES = {
-    SeeCustomField          => 'General',
-    AdminCustomField        => 'Admin',
-    AdminCustomFieldValues  => 'Admin',
-    ModifyCustomField       => 'Staff',
-};
-
-# Tell RT::ACE that this sort of object can get acls granted
-$RT::ACE::OBJECT_TYPES{'RT::CustomField'} = 1;
-
-__PACKAGE__->AddRights(%$RIGHTS);
-__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
-
-=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
-
-Adds the given rights to the list of possible rights.  This method
-should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRights {
-    my $self = shift;
-    my %new = @_;
-    $RIGHTS = { %$RIGHTS, %new };
-    %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
-                                      map { lc($_) => $_ } keys %new);
-}
-
-sub AvailableRights {
-    my $self = shift;
-    return $RIGHTS;
-}
-
-=head2 RightCategories
-
-Returns a hashref where the keys are rights for this type of object and the
-values are the category (General, Staff, Admin) the right falls into.
-
-=cut
-
-sub RightCategories {
-    return $RIGHT_CATEGORIES;
-}
-
-=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
-
-Adds the given right and category pairs to the list of right categories.  This
-method should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRightCategories {
-    my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
-    my %new = @_;
-    $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
-}
+__PACKAGE__->AddRight( General => SeeCustomField         => 'View custom fields'); # loc_pair
+__PACKAGE__->AddRight( Admin   => AdminCustomField       => 'Create, modify and delete custom fields'); # loc_pair
+__PACKAGE__->AddRight( Admin   => AdminCustomFieldValues => 'Create, modify and delete custom fields values'); # loc_pair
+__PACKAGE__->AddRight( Staff   => ModifyCustomField      => 'Add, modify and delete custom field values for objects'); # loc_pair
 
 =head1 NAME
 
diff --git a/lib/RT/Dashboard.pm b/lib/RT/Dashboard.pm
index ad4101f..8fafc1c 100644
--- a/lib/RT/Dashboard.pm
+++ b/lib/RT/Dashboard.pm
@@ -75,33 +75,18 @@ use base qw/RT::SharedSetting/;
 use RT::SavedSearch;
 
 use RT::System;
-RT::System::AddRights(
-    SubscribeDashboard => 'Subscribe to dashboards', #loc_pair
-
-    SeeDashboard       => 'View system dashboards', #loc_pair
-    CreateDashboard    => 'Create system dashboards', #loc_pair
-    ModifyDashboard    => 'Modify system dashboards', #loc_pair
-    DeleteDashboard    => 'Delete system dashboards', #loc_pair
-
-    SeeOwnDashboard    => 'View personal dashboards', #loc_pair
-    CreateOwnDashboard => 'Create personal dashboards', #loc_pair
-    ModifyOwnDashboard => 'Modify personal dashboards', #loc_pair
-    DeleteOwnDashboard => 'Delete personal dashboards', #loc_pair
-);
-
-RT::System::AddRightCategories(
-    SubscribeDashboard => 'Staff',
-
-    SeeDashboard       => 'General',
-    CreateDashboard    => 'Admin',
-    ModifyDashboard    => 'Admin',
-    DeleteDashboard    => 'Admin',
-
-    SeeOwnDashboard    => 'Staff',
-    CreateOwnDashboard => 'Staff',
-    ModifyOwnDashboard => 'Staff',
-    DeleteOwnDashboard => 'Staff',
-);
+'RT::System'->AddRight( Staff   => SubscribeDashboard => 'Subscribe to dashboards'); # loc_pair
+
+'RT::System'->AddRight( General => SeeDashboard       => 'View system dashboards'); # loc_pair
+'RT::System'->AddRight( Admin   => CreateDashboard    => 'Create system dashboards'); # loc_pair
+'RT::System'->AddRight( Admin   => ModifyDashboard    => 'Modify system dashboards'); # loc_pair
+'RT::System'->AddRight( Admin   => DeleteDashboard    => 'Delete system dashboards'); # loc_pair
+
+'RT::System'->AddRight( Staff   => SeeOwnDashboard    => 'View personal dashboards'); # loc_pair
+'RT::System'->AddRight( Staff   => CreateOwnDashboard => 'Create personal dashboards'); # loc_pair
+'RT::System'->AddRight( Staff   => ModifyOwnDashboard => 'Modify personal dashboards'); # loc_pair
+'RT::System'->AddRight( Staff   => DeleteOwnDashboard => 'Delete personal dashboards'); # loc_pair
+
 
 =head2 ObjectName
 
diff --git a/lib/RT/Group.pm b/lib/RT/Group.pm
index 8c65230..cd32b7f 100644
--- a/lib/RT/Group.pm
+++ b/lib/RT/Group.pm
@@ -73,6 +73,9 @@ use warnings;
 
 use base 'RT::Record';
 
+use Role::Basic 'with';
+with "RT::Record::Role::Rights";
+
 sub Table {'Groups'}
 
 
@@ -82,97 +85,19 @@ use RT::GroupMembers;
 use RT::Principals;
 use RT::ACL;
 
-use vars qw/$RIGHTS $RIGHT_CATEGORIES/;
-
-$RIGHTS = {
-    AdminGroup              => 'Modify group metadata or delete group',     # loc_pair
-    AdminGroupMembership    => 'Modify group membership roster',            # loc_pair
-    ModifyOwnMembership     => 'Join or leave group',                       # loc_pair
-    EditSavedSearches       => 'Create, modify and delete saved searches',  # loc_pair
-    ShowSavedSearches       => 'View saved searches',                       # loc_pair
-    SeeGroup                => 'View group',                                # loc_pair
-    SeeGroupDashboard       => 'View group dashboards',                     # loc_pair
-    CreateGroupDashboard    => 'Create group dashboards',                   # loc_pair
-    ModifyGroupDashboard    => 'Modify group dashboards',                   # loc_pair
-    DeleteGroupDashboard    => 'Delete group dashboards',                   # loc_pair
-};
-
-$RIGHT_CATEGORIES = {
-    AdminGroup              => 'Admin',
-    AdminGroupMembership    => 'Admin',
-    ModifyOwnMembership     => 'Staff',
-    EditSavedSearches       => 'Admin',
-    ShowSavedSearches       => 'Staff',
-    SeeGroup                => 'Staff',
-    SeeGroupDashboard       => 'Staff',
-    CreateGroupDashboard    => 'Admin',
-    ModifyGroupDashboard    => 'Admin',
-    DeleteGroupDashboard    => 'Admin',
-};
-
-# Tell RT::ACE that this sort of object can get acls granted
-$RT::ACE::OBJECT_TYPES{'RT::Group'} = 1;
-
-# TODO: This should be refactored out into an RT::ACLedObject or something
-# stuff the rights into a hash of rights that can exist.
-
-__PACKAGE__->AddRights(%$RIGHTS);
-__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
+__PACKAGE__->AddRight( Admin => AdminGroup           => 'Modify group metadata or delete group'); # loc_pair
+__PACKAGE__->AddRight( Admin => AdminGroupMembership => 'Modify group membership roster'); # loc_pair
+__PACKAGE__->AddRight( Staff => ModifyOwnMembership  => 'Join or leave group'); # loc_pair
+__PACKAGE__->AddRight( Admin => EditSavedSearches    => 'Create, modify and delete saved searches'); # loc_pair
+__PACKAGE__->AddRight( Staff => ShowSavedSearches    => 'View saved searches'); # loc_pair
+__PACKAGE__->AddRight( Staff => SeeGroup             => 'View group'); # loc_pair
+__PACKAGE__->AddRight( Staff => SeeGroupDashboard    => 'View group dashboards'); # loc_pair
+__PACKAGE__->AddRight( Admin => CreateGroupDashboard => 'Create group dashboards'); # loc_pair
+__PACKAGE__->AddRight( Admin => ModifyGroupDashboard => 'Modify group dashboards'); # loc_pair
+__PACKAGE__->AddRight( Admin => DeleteGroupDashboard => 'Delete group dashboards'); # loc_pair
 
 =head1 METHODS
 
-=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
-
-Adds the given rights to the list of possible rights.  This method
-should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRights {
-    my $self = shift;
-    my %new = @_;
-    $RIGHTS = { %$RIGHTS, %new };
-    %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
-                                      map { lc($_) => $_ } keys %new);
-}
-
-=head2 AvailableRights
-
-Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do
-
-=cut
-
-sub AvailableRights {
-    my $self = shift;
-    return($RIGHTS);
-}
-
-=head2 RightCategories
-
-Returns a hashref where the keys are rights for this type of object and the
-values are the category (General, Staff, Admin) the right falls into.
-
-=cut
-
-sub RightCategories {
-    return $RIGHT_CATEGORIES;
-}
-
-=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
-
-Adds the given right and category pairs to the list of right categories.  This
-method should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRightCategories {
-    my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
-    my %new = @_;
-    $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
-}
-
-
-
 =head2 SelfDescription
 
 Returns a user-readable description of what this group is for and what it's named.
diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
index ac6f9a5..ea6f060 100644
--- a/lib/RT/Interface/Web.pm
+++ b/lib/RT/Interface/Web.pm
@@ -2430,7 +2430,7 @@ sub ProcessACLChanges {
         my $obj;
         if ( $object_type eq 'RT::System' ) {
             $obj = $RT::System;
-        } elsif ( $RT::ACE::OBJECT_TYPES{$object_type} ) {
+        } elsif ( $object_type->DOES('RT::Record::Role::Rights') ) {
             $obj = $object_type->new( $session{'CurrentUser'} );
             $obj->Load($object_id);
             unless ( $obj->id ) {
@@ -2530,7 +2530,7 @@ sub ProcessACLs {
         my $obj;
         if ( $object_type eq 'RT::System' ) {
             $obj = $RT::System;
-        } elsif ( $RT::ACE::OBJECT_TYPES{$object_type} ) {
+        } elsif ( $object_type->DOES('RT::Record::Role::Rights') ) {
             $obj = $object_type->new( $session{'CurrentUser'} );
             $obj->Load($object_id);
             unless ( $obj->id ) {
diff --git a/lib/RT/Lifecycle/Ticket.pm b/lib/RT/Lifecycle/Ticket.pm
index e8f56e8..596832b 100644
--- a/lib/RT/Lifecycle/Ticket.pm
+++ b/lib/RT/Lifecycle/Ticket.pm
@@ -115,16 +115,10 @@ sub RegisterRights {
 
     require RT::ACE;
 
-    require RT::Queue;
-    my $RIGHTS = $RT::Queue::RIGHTS;
-
     while ( my ($right, $description) = each %rights ) {
-        next if exists $RIGHTS->{ $right }
-            or $RT::System::RIGHTS->{ $right };
+        next if RT::ACE->CanonicalizeRightName( $right );
 
-        $RIGHTS->{ $right } = $description;
-        RT::Queue->AddRightCategories( $right => 'Status' );
-        $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+        RT::Queue->AddRight( Status => $right => $description );
     }
 }
 
diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
index 0f32ad2..b3f6ed8 100644
--- a/lib/RT/Queue.pm
+++ b/lib/RT/Queue.pm
@@ -72,7 +72,8 @@ use base 'RT::Record';
 use Role::Basic 'with';
 with "RT::Record::Role::Lifecycle",
      "RT::Record::Role::Links" => { -excludes => ["_AddLinksOnCreate"] },
-     "RT::Record::Role::Roles";
+     "RT::Record::Role::Roles",
+     "RT::Record::Role::Rights";
 
 sub Table {'Queues'}
 
@@ -105,129 +106,37 @@ use RT::Interface::Email;
 # $self->loc('rejected'); # For the string extractor to get a string to localize
 # $self->loc('deleted'); # For the string extractor to get a string to localize
 
-
-our $RIGHTS = {
-    SeeQueue            => 'View queue',                                                # loc_pair
-    AdminQueue          => 'Create, modify and delete queue',                           # loc_pair
-    ShowACL             => 'Display Access Control List',                               # loc_pair
-    ModifyACL           => 'Create, modify and delete Access Control List entries',     # loc_pair
-    ModifyQueueWatchers => 'Modify queue watchers',                                     # loc_pair
-    SeeCustomField      => 'View custom field values',                                  # loc_pair
-    ModifyCustomField   => 'Modify custom field values',                                # loc_pair
-    AssignCustomFields  => 'Assign and remove queue custom fields',                     # loc_pair
-    ModifyTemplate      => 'Modify Scrip templates',                                    # loc_pair
-    ShowTemplate        => 'View Scrip templates',                                      # loc_pair
-
-    ModifyScrips        => 'Modify Scrips',                                             # loc_pair
-    ShowScrips          => 'View Scrips',                                               # loc_pair
-
-    ShowTicket          => 'View ticket summaries',                                     # loc_pair
-    ShowTicketComments  => 'View ticket private commentary',                            # loc_pair
-    ShowOutgoingEmail   => 'View exact outgoing email messages and their recipients',   # loc_pair
-
-    Watch               => 'Sign up as a ticket Requestor or ticket or queue Cc',       # loc_pair
-    WatchAsAdminCc      => 'Sign up as a ticket or queue AdminCc',                      # loc_pair
-    CreateTicket        => 'Create tickets',                                            # loc_pair
-    ReplyToTicket       => 'Reply to tickets',                                          # loc_pair
-    CommentOnTicket     => 'Comment on tickets',                                        # loc_pair
-    OwnTicket           => 'Own tickets',                                               # loc_pair
-    ModifyTicket        => 'Modify tickets',                                            # loc_pair
-    DeleteTicket        => 'Delete tickets',                                            # loc_pair
-    TakeTicket          => 'Take tickets',                                              # loc_pair
-    StealTicket         => 'Steal tickets',                                             # loc_pair
-    ReassignTicket      => 'Modify ticket owner on owned tickets',                      # loc_pair
-
-    ForwardMessage      => 'Forward messages outside of RT',                            # loc_pair
-};
-
-our $RIGHT_CATEGORIES = {
-    SeeQueue            => 'General',
-    AdminQueue          => 'Admin',
-    ShowACL             => 'Admin',
-    ModifyACL           => 'Admin',
-    ModifyQueueWatchers => 'Admin',
-    SeeCustomField      => 'General',
-    ModifyCustomField   => 'Staff',
-    AssignCustomFields  => 'Admin',
-    ModifyTemplate      => 'Admin',
-    ShowTemplate        => 'Admin',
-    ModifyScrips        => 'Admin',
-    ShowScrips          => 'Admin',
-    ShowTicket          => 'General',
-    ShowTicketComments  => 'Staff',
-    ShowOutgoingEmail   => 'Staff',
-    Watch               => 'General',
-    WatchAsAdminCc      => 'Staff',
-    CreateTicket        => 'General',
-    ReplyToTicket       => 'General',
-    CommentOnTicket     => 'General',
-    OwnTicket           => 'Staff',
-    ModifyTicket        => 'Staff',
-    DeleteTicket        => 'Staff',
-    TakeTicket          => 'Staff',
-    StealTicket         => 'Staff',
-    ReassignTicket      => 'Staff',
-    ForwardMessage      => 'Staff',
-};
-
-# Tell RT::ACE that this sort of object can get acls granted
-$RT::ACE::OBJECT_TYPES{'RT::Queue'} = 1;
-
-# TODO: This should be refactored out into an RT::ACLedObject or something
-# stuff the rights into a hash of rights that can exist.
-
-__PACKAGE__->AddRights(%$RIGHTS);
-__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
-
-=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
-
-Adds the given rights to the list of possible rights.  This method
-should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRights {
-    my $self = shift;
-    my %new = @_;
-    $RIGHTS = { %$RIGHTS, %new };
-    %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
-                                      map { lc($_) => $_ } keys %new);
-}
-
-=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
-
-Adds the given right and category pairs to the list of right categories.  This
-method should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRightCategories {
-    my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
-    my %new = @_;
-    $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
-}
-
-=head2 AvailableRights
-
-Returns a hash of available rights for this object. The keys are the right names and the values are a description of what the rights do
-
-=cut
-
-sub AvailableRights {
-    my $self = shift;
-    return($RIGHTS);
-}
-
-=head2 RightCategories
-
-Returns a hashref where the keys are rights for this type of object and the
-values are the category (General, Staff, Admin) the right falls into.
-
-=cut
-
-sub RightCategories {
-    return $RIGHT_CATEGORIES;
-}
+__PACKAGE__->AddRight( General => SeeQueue            => 'View queue' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => AdminQueue          => 'Create, modify and delete queue' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => ShowACL             => 'Display Access Control List' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => ModifyACL           => 'Create, modify and delete Access Control List entries' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => ModifyQueueWatchers => 'Modify queue watchers' ); # loc_pair
+__PACKAGE__->AddRight( General => SeeCustomField      => 'View custom field values' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => ModifyCustomField   => 'Modify custom field values' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => AssignCustomFields  => 'Assign and remove queue custom fields' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => ModifyTemplate      => 'Modify Scrip templates' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => ShowTemplate        => 'View Scrip templates' ); # loc_pair
+
+__PACKAGE__->AddRight( Admin   => ModifyScrips        => 'Modify Scrips' ); # loc_pair
+__PACKAGE__->AddRight( Admin   => ShowScrips          => 'View Scrips' ); # loc_pair
+
+__PACKAGE__->AddRight( General => ShowTicket          => 'View ticket summaries' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => ShowTicketComments  => 'View ticket private commentary' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => ShowOutgoingEmail   => 'View exact outgoing email messages and their recipients' ); # loc_pair
+
+__PACKAGE__->AddRight( General => Watch               => 'Sign up as a ticket Requestor or ticket or queue Cc' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => WatchAsAdminCc      => 'Sign up as a ticket or queue AdminCc' ); # loc_pair
+__PACKAGE__->AddRight( General => CreateTicket        => 'Create tickets' ); # loc_pair
+__PACKAGE__->AddRight( General => ReplyToTicket       => 'Reply to tickets' ); # loc_pair
+__PACKAGE__->AddRight( General => CommentOnTicket     => 'Comment on tickets' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => OwnTicket           => 'Own tickets' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => ModifyTicket        => 'Modify tickets' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => DeleteTicket        => 'Delete tickets' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => TakeTicket          => 'Take tickets' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => StealTicket         => 'Steal tickets' ); # loc_pair
+__PACKAGE__->AddRight( Staff   => ReassignTicket      => 'Modify ticket owner on owned tickets' ); # loc_pair
+
+__PACKAGE__->AddRight( Staff   => ForwardMessage      => 'Forward messages outside of RT' ); # loc_pair
 
 =head2 Create(ARGS)
 
diff --git a/lib/RT/Lifecycle/Ticket.pm b/lib/RT/Record/Role/Rights.pm
similarity index 57%
copy from lib/RT/Lifecycle/Ticket.pm
copy to lib/RT/Record/Role/Rights.pm
index e8f56e8..5870eae 100644
--- a/lib/RT/Lifecycle/Ticket.pm
+++ b/lib/RT/Record/Role/Rights.pm
@@ -49,83 +49,85 @@
 use strict;
 use warnings;
 
-package RT::Lifecycle::Ticket;
+package RT::Record::Role::Rights;
+use Role::Basic;
+use Scalar::Util qw(blessed);
 
-use base qw(RT::Lifecycle);
+=head1 NAME
 
-=head2 Queues
+RT::Record::Role::Rights - Common methods for records which can provide rights
 
-Returns L<RT::Queues> collection with queues that use this lifecycle.
+=head1 DESCRIPTION
 
-=cut
+=head1 REQUIRES
 
-sub Queues {
-    my $self = shift;
-    require RT::Queues;
-    my $queues = RT::Queues->new( RT->SystemUser );
-    $queues->Limit( FIELD => 'Lifecycle', VALUE => $self->Name );
-    return $queues;
-}
+=head2 L<RT::Record::Role>
 
-=head3 DefaultOnMerge
+=cut
 
-Returns the status that should be used when tickets
-are merged.
+with 'RT::Record::Role';
 
-=cut
+=head1 PROVIDES
 
-sub DefaultOnMerge {
-    my $self = shift;
-    return $self->DefaultStatus('on_merge');
-}
+=cut
 
-=head3 ReminderStatusOnOpen
+=head2 AddRight C<CATEGORY>, C<RIGHT>, C<DESCRIPTION>
 
-Returns the status that should be used when reminders are opened.
+Adds the given rights to the list of possible rights.  This method
+should be called during server startup, not at runtime.
 
 =cut
 
-sub ReminderStatusOnOpen {
-    my $self = shift;
-    return $self->DefaultStatus('reminder_on_open') || 'open';
+sub AddRight {
+    my $class = shift;
+    $class = ref($class) || $class;
+    my ($category, $name, $description) = @_;
+
+    require RT::ACE;
+    if (exists $RT::ACE::RIGHTS{$class}{lc $name}) {
+        warn "Duplicate right '$name' found";
+        return;
+    }
+
+    $RT::ACE::RIGHTS{$class}{lc $name} = {
+        Name        => $name,
+        Category    => $category,
+        Description => $description,
+    };
 }
 
-=head3 ReminderStatusOnResolve
+=head2 AvailableRights
 
-Returns the status that should be used when reminders are resolved.
+Returns a hashref of available rights for this object. The keys are the
+right names and the values are a description of what the rights do.
 
 =cut
 
-sub ReminderStatusOnResolve {
+sub AvailableRights {
     my $self = shift;
-    return $self->DefaultStatus('reminder_on_resolve') || 'resolved';
+    my $class = ref($self) || $self;
+
+    my %rights;
+    $rights{$_->{Name}} = $_->{Description}
+        for values %{$RT::ACE::RIGHTS{$class} || {} };
+    return \%rights;
 }
 
-=head2 RegisterRights
+=head2 RightCategories
 
-Ticket lifecycle rights are registered (and thus grantable) at the queue
-level.
+Returns a hashref where the keys are rights for this type of object and the
+values are the category (General, Staff, Admin) the right falls into.
 
 =cut
 
-sub RegisterRights {
+sub RightCategories {
     my $self = shift;
+    my $class = ref($self) || $self;
 
-    my %rights = $self->RightsDescription( 'ticket' );
-
-    require RT::ACE;
-
-    require RT::Queue;
-    my $RIGHTS = $RT::Queue::RIGHTS;
-
-    while ( my ($right, $description) = each %rights ) {
-        next if exists $RIGHTS->{ $right }
-            or $RT::System::RIGHTS->{ $right };
-
-        $RIGHTS->{ $right } = $description;
-        RT::Queue->AddRightCategories( $right => 'Status' );
-        $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
-    }
+    my %rights;
+    $rights{$_->{Name}} = $_->{Category}
+        for values %{ $RT::ACE::RIGHTS{$class} || {} };
+    return \%rights;
 }
 
 1;
diff --git a/lib/RT/System.pm b/lib/RT/System.pm
index 7b0a4cb..3493a7a 100644
--- a/lib/RT/System.pm
+++ b/lib/RT/System.pm
@@ -73,115 +73,61 @@ use warnings;
 use base qw/RT::Record/;
 
 use Role::Basic 'with';
-with "RT::Record::Role::Roles";
+with "RT::Record::Role::Roles",
+     "RT::Record::Role::Rights" => { -excludes => [qw/AvailableRights RightCategories/] };
 
 use RT::ACL;
 use RT::ACE;
 
-# System rights are rights granted to the whole system
-# XXX TODO Can't localize these outside of having an object around.
-our $RIGHTS = {
-    SuperUser              => 'Do anything and everything',           # loc_pair
-    AdminUsers     => 'Create, modify and delete users',              # loc_pair
-    ModifySelf     => "Modify one's own RT account",                  # loc_pair
-    ShowArticlesMenu => "Show Articles menu",     # loc_pair
-    ShowConfigTab => "Show Admin menu",     # loc_pair
-    ShowApprovalsTab => "Show Approvals tab",     # loc_pair
-    ShowGlobalTemplates => "Show global templates",     # loc_pair
-    LoadSavedSearch => "Allow loading of saved searches",     # loc_pair
-    CreateSavedSearch => "Allow creation of saved searches",      # loc_pair
-    ExecuteCode => "Allow writing Perl code in templates, scrips, etc", # loc_pair
-};
-
-our $RIGHT_CATEGORIES = {
-    SuperUser              => 'Admin',
-    AdminUsers             => 'Admin',
-    ModifySelf             => 'Staff',
-    ShowArticlesMenu       => 'Staff',
-    ShowConfigTab          => 'Admin',
-    ShowApprovalsTab       => 'Admin',
-    ShowGlobalTemplates    => 'Staff',
-    LoadSavedSearch        => 'General',
-    CreateSavedSearch      => 'General',
-    ExecuteCode            => 'Admin',
-};
-
-# Tell RT::ACE that this sort of object can get acls granted
-$RT::ACE::OBJECT_TYPES{'RT::System'} = 1;
-
-__PACKAGE__->AddRights(%$RIGHTS);
-__PACKAGE__->AddRightCategories(%$RIGHT_CATEGORIES);
+__PACKAGE__->AddRight( Admin   => SuperUser           => 'Do anything and everything'); # loc_pair
+__PACKAGE__->AddRight( Admin   => AdminUsers          => 'Create, modify and delete users'); # loc_pair
+__PACKAGE__->AddRight( Staff   => ModifySelf          => "Modify one's own RT account"); # loc_pair
+__PACKAGE__->AddRight( Staff   => ShowArticlesMenu    => 'Show Articles menu'); # loc_pair
+__PACKAGE__->AddRight( Admin   => ShowConfigTab       => 'Show Configuration tab'); # loc_pair
+__PACKAGE__->AddRight( Admin   => ShowApprovalsTab    => 'Show Approvals tab'); # loc_pair
+__PACKAGE__->AddRight( Staff   => ShowGlobalTemplates => 'Show global templates'); # loc_pair
+__PACKAGE__->AddRight( General => LoadSavedSearch     => 'Allow loading of saved searches'); # loc_pair
+__PACKAGE__->AddRight( General => CreateSavedSearch   => 'Allow creation of saved searches'); # loc_pair
+__PACKAGE__->AddRight( Admin   => ExecuteCode         => 'Allow writing Perl code in templates, scrips, etc'); # loc_pair
 
 =head2 AvailableRights
 
-Returns a hash of available rights for this object.
-The keys are the right names and the values are a
-description of what the rights do.
+Returns a hashref of available rights for this object.  The keys are the
+right names and the values are a description of what the rights do.
 
-This method as well returns rights of other RT objects,
-like L<RT::Queue> or L<RT::Group>. To allow users to apply
-those rights globally.
+This method as well returns rights of other RT objects, like
+L<RT::Queue> or L<RT::Group>, to allow users to apply those rights
+globally.
 
-If an L<RT::Principal> is passed as the first argument, the available rights
-will be limited to ones which make sense for the principal.  Currently only
-role groups are supported and rights announced by object types to which the
-role group doesn't apply are not returned.
+If an L<RT::Principal> is passed as the first argument, the available
+rights will be limited to ones which make sense for the principal.
+Currently only role groups are supported and rights announced by object
+types to which the role group doesn't apply are not returned.
 
 =cut
 
 sub AvailableRights {
-    my $self        = shift;
-    my $principal   = shift;
-    my @types       = keys %RT::ACE::OBJECT_TYPES;
-
-    # Include global system rights by default
-    my %rights = %{ $RIGHTS };
+    my $self = shift;
+    my $principal = shift;
+    my $class = ref($self) || $self;
 
-    # Only return rights on classes which support the role asked for
+    my @rights;
     if ($principal and $principal->IsRoleGroup) {
         my $role = $principal->Object->Type;
-        @types   = grep { $_->DOES('RT::Record::Role::Roles') and $_->HasRole($role) } @types;
-        %rights  = ();
+        for my $class (keys %RT::ACE::RIGHTS) {
+            next unless $class->DOES('RT::Record::Role::Roles') and $class->HasRole($role);
+            push @rights, values %{ $RT::ACE::RIGHTS{$class} };
+        }
+    } else {
+        @rights = map {values %{$_}} values %RT::ACE::RIGHTS;
     }
 
-    # Build a merged list of system wide rights, queue rights, group rights, etc.
-    %rights = (
-        %rights,
-        %{ $self->_ForACEObjectTypes(\@types => 'AvailableRights', @_) },
-    );
-    delete $rights{ExecuteCode} if RT->Config->Get('DisallowExecuteCode');
-
-    return(\%rights);
-}
+    my %rights;
+    $rights{$_->{Name}} = $_->{Description} for @rights;
 
-sub _ForACEObjectTypes {
-    my $self   = shift;
-    my $types  = shift || [];
-    my $method = shift;
-    return {} unless @$types and $method;
-
-    my %data;
-    for my $class (sort @$types) {
-        next unless $RT::ACE::OBJECT_TYPES{$class};
-
-        # Skip ourselves otherwise we'd loop infinitely
-        next if $class eq 'RT::System';
-
-        my $object = $class->new(RT->SystemUser);
-
-        unless ($object->can($method)) {
-            RT->Logger->error("RT::ACE object type $class doesn't support the $method method! Skipping.");
-            next;
-        }
-
-        # embrace and extend
-        %data = (
-            %data,
-            %{ $object->$method(@_) || {} },
-        );
-    }
+    delete $rights{ExecuteCode} if RT->Config->Get('DisallowExecuteCode');
 
-    return \%data;
+    return \%rights;
 }
 
 =head2 RightCategories
@@ -193,42 +139,12 @@ values are the category (General, Staff, Admin) the right falls into.
 
 sub RightCategories {
     my $self = shift;
+    my $class = ref($self) || $self;
 
-    # Build a merged list of all right categories system wide, per-queue, per-group, etc.
-    my %categories = (
-        %{ $RIGHT_CATEGORIES },
-        %{ $self->_ForACEObjectTypes([keys %RT::ACE::OBJECT_TYPES] => 'RightCategories') },
-    );
-
-    return \%categories;
-}
-
-=head2 AddRights C<RIGHT>, C<DESCRIPTION> [, ...]
-
-Adds the given rights to the list of possible rights.  This method
-should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRights {
-    my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
-    my %new = @_;
-    $RIGHTS = { %$RIGHTS, %new };
-    %RT::ACE::LOWERCASERIGHTNAMES = ( %RT::ACE::LOWERCASERIGHTNAMES,
-                                      map { lc($_) => $_ } keys %new);
-}
-
-=head2 AddRightCategories C<RIGHT>, C<CATEGORY> [, ...]
-
-Adds the given right and category pairs to the list of right categories.  This
-method should be called during server startup, not at runtime.
-
-=cut
-
-sub AddRightCategories {
-    my $self = shift if ref $_[0] or $_[0] eq __PACKAGE__;
-    my %new = @_;
-    $RIGHT_CATEGORIES = { %$RIGHT_CATEGORIES, %new };
+    my %rights;
+    $rights{$_->{Name}} = $_->{Category}
+        for map {values %{$_}} values %RT::ACE::RIGHTS;
+    return \%rights;
 }
 
 sub _Init {
diff --git a/t/api/group-rights.t b/t/api/group-rights.t
index 0494c28..4f5f03d 100644
--- a/t/api/group-rights.t
+++ b/t/api/group-rights.t
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 use RT::Test nodata => 1, tests => 114;
 
-RT::Group->AddRights(
+RT::Group->AddRight( General =>
     'RTxGroupRight' => 'Just a right for testing rights',
 );
 
diff --git a/t/api/groups.t b/t/api/groups.t
index 013e192..c2e7fc5 100644
--- a/t/api/groups.t
+++ b/t/api/groups.t
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 use RT::Test nodata => 1, tests => 27;
 
-RT::Group->AddRights(
+RT::Group->AddRight( General =>
     'RTxGroupRight' => 'Just a right for testing rights',
 );
 
diff --git a/t/api/system.t b/t/api/system.t
index b960392..f3d9226 100644
--- a/t/api/system.t
+++ b/t/api/system.t
@@ -11,7 +11,7 @@ BEGIN{
 # Skipping most of the methods added just to make RT::System
 # look like RT::Record.
 
-can_ok('RT::System', qw( AvailableRights RightCategories AddRights AddRightCategories
+can_ok('RT::System', qw( AvailableRights RightCategories AddRight
                          id Id SubjectTag Name QueueCacheNeedsUpdate AddUpgradeHistory
                          UpgradeHistory ));
 
diff --git a/t/api/users.t b/t/api/users.t
index 1f3a487..e65f9a9 100644
--- a/t/api/users.t
+++ b/t/api/users.t
@@ -2,7 +2,7 @@ use strict;
 use warnings;
 use RT::Test tests => 10;
 
-RT::System->AddRights(
+RT::System->AddRight( General =>
     'RTxUserRight' => 'Just a right for testing rights',
 );
 

-----------------------------------------------------------------------


More information about the Rt-commit mailing list