+sub IsGroup {
+ my $self = shift;
+ if ( defined $self->PrincipalType &&
+ $self->PrincipalType eq 'Group' ) {
+ return 1;
+ }
+ return undef;
+}
+
+
+
+=head2 IsUser
+
+Returns true if this principal is a User.
+Returns undef, otherwise
+
+=cut
+
+sub IsUser {
+ my $self = shift;
+ if ($self->PrincipalType eq 'User') {
+ return(1);
+ }
+ else {
+ return undef;
+ }
+}
+
+
+
+=head2 Object
+
+Returns the user or group associated with this principal
+
+=cut
+
+sub Object {
+ my $self = shift;
+
+ unless ( $self->{'object'} ) {
+ if ( $self->IsUser ) {
+ $self->{'object'} = RT::User->new($self->CurrentUser);
+ }
+ elsif ( $self->IsGroup ) {
+ $self->{'object'} = RT::Group->new($self->CurrentUser);
+ }
+ else {
+ $RT::Logger->crit("Found a principal (".$self->Id.") that was neither a user nor a group");
+ return(undef);
+ }
+ $self->{'object'}->Load( $self->ObjectId() );
+ }
+ return ($self->{'object'});
+
+
+}
+
+
+
+=head2 GrantRight { Right => RIGHTNAME, Object => undef }
+
+A helper function which calls RT::ACE->Create
+
+
+
+ Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's
+ false.
+
+=cut
+
+sub GrantRight {
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ @_
+ );
+
+ return (0, "Permission denied") if $args{'Right'} eq 'ExecuteCode'
+ and RT->Config->Get('DisallowExecuteCode');
+
+ #ACL check handled in ACE.pm
+ my $ace = RT::ACE->new( $self->CurrentUser );
+
+ my $type = $self->_GetPrincipalTypeForACL();
+
+ RT->System->QueueCacheNeedsUpdate(1) if $args{'Right'} eq 'SeeQueue';
+
+ # If it's a user, we really want to grant the right to their
+ # user equivalence group
+ return $ace->Create(
+ RightName => $args{'Right'},
+ Object => $args{'Object'},
+ PrincipalType => $type,
+ PrincipalId => $self->Id,
+ );
+}
+
+
+=head2 RevokeRight { Right => "RightName", Object => "object" }
+
+Delete a right that a user has
+
+
+ Returns a tuple of (STATUS, MESSAGE); If the call succeeded, STATUS is true. Otherwise it's
+ false.
+
+
+=cut
+
+sub RevokeRight {
+
+ my $self = shift;
+ my %args = (
+ Right => undef,
+ Object => undef,
+ @_
+ );
+
+ #if we haven't specified any sort of right, we're talking about a global right
+ if (!defined $args{'Object'} && !defined $args{'ObjectId'} && !defined $args{'ObjectType'}) {
+ $args{'Object'} = $RT::System;
+ }
+ #ACL check handled in ACE.pm
+ my $type = $self->_GetPrincipalTypeForACL();
+
+ my $ace = RT::ACE->new( $self->CurrentUser );
+ my ($status, $msg) = $ace->LoadByValues(
+ RightName => $args{'Right'},
+ Object => $args{'Object'},
+ PrincipalType => $type,
+ PrincipalId => $self->Id
+ );
+
+ if ( not $status and $msg =~ /Invalid right/ ) {
+ $RT::Logger->warn("Tried to revoke the invalid right '$args{Right}', ignoring it.");
+ return (1);
+ }
+
+ RT->System->QueueCacheNeedsUpdate(1) if $args{'Right'} eq 'SeeQueue';
+ return ($status, $msg) unless $status;
+ return $ace->Delete;
+}
+
+
+
+=head2 HasRight (Right => 'right' Object => undef)
+
+Checks to see whether this principal has the right "Right" for the Object
+specified. This takes the params:
+
+=over 4
+
+=item Right
+
+name of a right
+
+=item Object
+
+an RT style object (->id will get its id)
+
+=back
+
+Returns 1 if a matching ACE was found. Returns undef if no ACE was found.
+
+Use L</HasRights> to fill a fast cache, especially if you're going to
+check many different rights with the same principal and object.
+
+=cut
+
+sub HasRight {
+
+ my $self = shift;
+ my %args = ( Right => undef,
+ Object => undef,
+ EquivObjects => undef,
+ @_,
+ );
+
+ # RT's SystemUser always has all rights
+ if ( $self->id == RT->SystemUser->id ) {
+ return 1;
+ }
+
+ if ( my $right = RT::ACE->CanonicalizeRightName( $args{'Right'} ) ) {
+ $args{'Right'} = $right;
+ } else {
+ $RT::Logger->error(
+ "Invalid right. Couldn't canonicalize right '$args{'Right'}'");
+ return undef;
+ }
+
+ return undef if $args{'Right'} eq 'ExecuteCode'
+ and RT->Config->Get('DisallowExecuteCode');
+
+ $args{'EquivObjects'} = [ @{ $args{'EquivObjects'} } ]
+ if $args{'EquivObjects'};
+
+ if ( $self->__Value('Disabled') ) {
+ $RT::Logger->debug( "Disabled User #"
+ . $self->id
+ . " failed access check for "
+ . $args{'Right'} );
+ return (undef);
+ }
+
+ if ( eval { $args{'Object'}->id } ) {
+ push @{ $args{'EquivObjects'} }, $args{'Object'};
+ } else {
+ $RT::Logger->crit("HasRight called with no valid object");
+ return (undef);
+ }
+
+ {
+ my $cached = $_ACL_CACHE->fetch(
+ $self->id .';:;'. ref($args{'Object'}) .'-'. $args{'Object'}->id
+ );
+ return $cached->{'SuperUser'} || $cached->{ $args{'Right'} }
+ if $cached;
+ }
+
+ unshift @{ $args{'EquivObjects'} },
+ $args{'Object'}->ACLEquivalenceObjects;
+
+ unshift @{ $args{'EquivObjects'} }, $RT::System
+ unless $self->can('_IsOverrideGlobalACL')
+ && $self->_IsOverrideGlobalACL( $args{'Object'} );
+
+ # If we've cached a win or loss for this lookup say so
+
+# Construct a hashkeys to cache decisions:
+# 1) full_hashkey - key for any result and for full combination of uid, right and objects
+# 2) short_hashkey - one key for each object to store positive results only, it applies
+# only to direct group rights and partly to role rights
+ my $full_hashkey = join (";:;", $self->id, $args{'Right'});
+ foreach ( @{ $args{'EquivObjects'} } ) {
+ my $ref_id = $self->_ReferenceId($_);
+ $full_hashkey .= ";:;".$ref_id;
+
+ my $short_hashkey = join(";:;", $self->id, $args{'Right'}, $ref_id);
+ my $cached_answer = $_ACL_CACHE->fetch($short_hashkey);
+ return $cached_answer > 0 if defined $cached_answer;
+ }
+
+ {
+ my $cached_answer = $_ACL_CACHE->fetch($full_hashkey);
+ return $cached_answer > 0 if defined $cached_answer;
+ }