summaryrefslogtreecommitdiff
path: root/rt/lib/RT/User_Overlay.pm
diff options
context:
space:
mode:
Diffstat (limited to 'rt/lib/RT/User_Overlay.pm')
-rw-r--r--rt/lib/RT/User_Overlay.pm1311
1 files changed, 901 insertions, 410 deletions
diff --git a/rt/lib/RT/User_Overlay.pm b/rt/lib/RT/User_Overlay.pm
index db3964cd3..73c3bdd5b 100644
--- a/rt/lib/RT/User_Overlay.pm
+++ b/rt/lib/RT/User_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::User - RT User object
@@ -59,6 +58,11 @@
=head1 METHODS
+=begin testing
+
+ok(require RT::User);
+
+=end testing
=cut
@@ -69,12 +73,19 @@ package RT::User;
use strict;
no warnings qw(redefine);
+use vars qw(%_USERS_KEY_CACHE);
+
+%_USERS_KEY_CACHE = ();
+
use Digest::MD5;
use RT::Principals;
use RT::ACE;
use RT::Interface::Email;
use Encode;
+# {{{ sub _Accessible
+
+
sub _OverlayAccessible {
{
@@ -99,9 +110,62 @@ sub _OverlayAccessible {
+# }}}
+
+# {{{ sub Create
+
=head2 Create { PARAMHASH }
+=begin testing
+
+# Make sure we can create a user
+
+my $u1 = RT::User->new($RT::SystemUser);
+is(ref($u1), 'RT::User');
+my ($id, $msg) = $u1->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok ($id, "Creating user CreateTest1 - " . $msg );
+
+# Make sure we can't create a second user with the same name
+my $u2 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u2->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-2@example.com');
+ok (!$id, $msg);
+
+
+# Make sure we can't create a second user with the same EmailAddress address
+my $u3 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u3->Create(Name => 'CreateTest2'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok (!$id, $msg);
+
+# Make sure we can create a user with no EmailAddress address
+my $u4 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u4->Create(Name => 'CreateTest3'.$$);
+ok ($id, $msg);
+
+# make sure we can create a second user with no EmailAddress address
+my $u5 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u5->Create(Name => 'CreateTest4'.$$);
+ok ($id, $msg);
+
+# make sure we can create a user with a blank EmailAddress address
+my $u6 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u6->Create(Name => 'CreateTest6'.$$, EmailAddress => '');
+ok ($id, $msg);
+# make sure we can create a second user with a blankEmailAddress address
+my $u7 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u7->Create(Name => 'CreateTest7'.$$, EmailAddress => '');
+ok ($id, $msg);
+
+# Can we change the email address away from from "";
+($id,$msg) = $u7->SetEmailAddress('foo@bar'.$$);
+ok ($id, $msg);
+# can we change the address back to "";
+($id,$msg) = $u7->SetEmailAddress('');
+ok ($id, $msg);
+is ($u7->EmailAddress, '');
+
+
+=end testing
=cut
@@ -121,7 +185,7 @@ sub Create {
#Check the ACL
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
- return ( 0, $self->loc('Permission Denied') );
+ return ( 0, $self->loc('No permission to create users') );
}
@@ -136,7 +200,9 @@ sub Create {
- my $privileged = delete $args{'Privileged'};
+ # Privileged is no longer a column in users
+ my $privileged = $args{'Privileged'};
+ delete $args{'Privileged'};
if ($args{'CryptedPassword'} ) {
@@ -146,8 +212,8 @@ sub Create {
elsif ( !$args{'Password'} ) {
$args{'Password'} = '*NO-PASSWORD*';
}
- elsif ( length( $args{'Password'} ) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [_1] characters long",RT->Config->Get('MinimumPasswordLength')) );
+ elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long",$RT::MinimumPasswordLength) );
}
else {
@@ -157,6 +223,8 @@ sub Create {
#TODO Specify some sensible defaults.
unless ( $args{'Name'} ) {
+ use Data::Dumper;
+ $RT::Logger->crit(Dumper \%args);
return ( 0, $self->loc("Must specify 'Name' attribute") );
}
@@ -166,8 +234,8 @@ sub Create {
$TempUser->Load( $args{'Name'} );
return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
- my ($val, $message) = $self->ValidateEmailAddress( $args{'EmailAddress'} );
- return (0, $message) unless ( $val );
+ return ( 0, $self->loc('Email address in use') )
+ unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
}
else {
$RT::Logger->warning( "$self couldn't check for pre-existing users");
@@ -256,7 +324,7 @@ sub Create {
if ( $record_transaction ) {
- $self->_NewTransaction( Type => "Create" );
+ $self->_NewTransaction( Type => "Create" );
}
$RT::Handle->Commit;
@@ -264,6 +332,12 @@ sub Create {
return ( $id, $self->loc('User created') );
}
+# }}}
+
+
+
+# {{{ SetPrivileged
+
=head2 SetPrivileged BOOL
If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
@@ -271,6 +345,20 @@ Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
Returns a standard RT tuple of (val, msg);
+=begin testing
+
+
+ok(my $user = RT::User->new($RT::SystemUser));
+ok($user->Load('root'), "Loaded user 'root'");
+ok($user->Privileged, "User 'root' is privileged");
+ok(my ($v,$m) = $user->SetPrivileged(0));
+ok ($v ==1, "Set unprivileged suceeded ($m)");
+ok(!$user->Privileged, "User 'root' is no longer privileged");
+ok(my ($v2,$m2) = $user->SetPrivileged(1));
+ok ($v2 ==1, "Set privileged suceeded ($m2");
+ok($user->Privileged, "User 'root' is privileged again");
+
+=end testing
=cut
@@ -282,9 +370,9 @@ sub SetPrivileged {
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
return ( 0, $self->loc('Permission Denied') );
}
-
my $priv = RT::Group->new($self->CurrentUser);
$priv->LoadSystemInternalGroup('Privileged');
+
unless ($priv->Id) {
$RT::Logger->crit("Could not find Privileged pseudogroup");
return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
@@ -297,14 +385,13 @@ sub SetPrivileged {
return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
}
- my $principal = $self->PrincipalId;
if ($val) {
- if ($priv->HasMember($principal)) {
+ if ($priv->HasMember($self->PrincipalObj)) {
#$RT::Logger->debug("That user is already privileged");
return (0,$self->loc("That user is already privileged"));
}
- if ($unpriv->HasMember($principal)) {
- $unpriv->_DeleteMember($principal);
+ if ($unpriv->HasMember($self->PrincipalObj)) {
+ $unpriv->_DeleteMember($self->PrincipalId);
} else {
# if we had layered transactions, life would be good
# sadly, we have to just go ahead, even if something
@@ -312,7 +399,7 @@ sub SetPrivileged {
$RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
"unprivileged. something is drastically wrong.");
}
- my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $principal);
+ my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
if ($status) {
return (1, $self->loc("That user is now privileged"));
} else {
@@ -320,12 +407,12 @@ sub SetPrivileged {
}
}
else {
- if ($unpriv->HasMember($principal)) {
+ if ($unpriv->HasMember($self->PrincipalObj)) {
#$RT::Logger->debug("That user is already unprivileged");
return (0,$self->loc("That user is already unprivileged"));
}
- if ($priv->HasMember($principal)) {
- $priv->_DeleteMember( $principal );
+ if ($priv->HasMember($self->PrincipalObj)) {
+ $priv->_DeleteMember( $self->PrincipalId);
} else {
# if we had layered transactions, life would be good
# sadly, we have to just go ahead, even if something
@@ -333,7 +420,7 @@ sub SetPrivileged {
$RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
"unprivileged. something is drastically wrong.");
}
- my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $principal);
+ my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
if ($status) {
return (1, $self->loc("That user is now unprivileged"));
} else {
@@ -342,6 +429,10 @@ sub SetPrivileged {
}
}
+# }}}
+
+# {{{ Privileged
+
=head2 Privileged
Returns true if this user is privileged. Returns undef otherwise.
@@ -352,7 +443,7 @@ sub Privileged {
my $self = shift;
my $priv = RT::Group->new($self->CurrentUser);
$priv->LoadSystemInternalGroup('Privileged');
- if ( $priv->HasMember( $self->PrincipalId ) ) {
+ if ($priv->HasMember($self->PrincipalObj)) {
return(1);
}
else {
@@ -360,6 +451,10 @@ sub Privileged {
}
}
+# }}}
+
+# {{{ sub _BootstrapCreate
+
#create a user without validating _any_ data.
#To be used only on database init.
@@ -409,6 +504,10 @@ sub _BootstrapCreate {
return ( $id, 'User created' );
}
+# }}}
+
+# {{{ sub Delete
+
sub Delete {
my $self = shift;
@@ -416,35 +515,40 @@ sub Delete {
}
+# }}}
+
+# {{{ sub Load
+
=head2 Load
Load a user object from the database. Takes a single argument.
-If the argument is numerical, load by the column 'id'. If a user
-object or its subclass passed then loads the same user by id.
-Otherwise, load by the "Name" column which is the user's textual
-username.
+If the argument is numerical, load by the column 'id'. Otherwise, load by
+the "Name" column which is the user's textual username.
=cut
sub Load {
- my $self = shift;
+ my $self = shift;
my $identifier = shift || return undef;
+ #if it's an int, load by id. otherwise, load by name.
if ( $identifier !~ /\D/ ) {
- return $self->SUPER::LoadById( $identifier );
- }
- elsif ( UNIVERSAL::isa( $identifier, 'RT::User' ) ) {
- return $self->SUPER::LoadById( $identifier->Id );
+ $self->SUPER::LoadById($identifier);
}
else {
- return $self->LoadByCol( "Name", $identifier );
+ $self->LoadByCol( "Name", $identifier );
}
}
+# }}}
+
+# {{{ sub LoadByEmail
+
=head2 LoadByEmail
Tries to load this user object from the database by the user's email address.
+
=cut
sub LoadByEmail {
@@ -458,15 +562,18 @@ sub LoadByEmail {
$address = $self->CanonicalizeEmailAddress($address);
- #$RT::Logger->debug("Trying to load an email address: $address");
+ #$RT::Logger->debug("Trying to load an email address: $address\n");
return $self->LoadByCol( "EmailAddress", $address );
}
+# }}}
+
+# {{{ LoadOrCreateByEmail
+
=head2 LoadOrCreateByEmail ADDRESS
Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
-the provided email address and loads them. Address can be provided either as L<Email::Address> object
-or string which is parsed using the module.
+the provided email address. and loads them.
Returns a tuple of the user's id and a status message.
0 will be returned in place of the user's id in case of failure.
@@ -477,45 +584,54 @@ sub LoadOrCreateByEmail {
my $self = shift;
my $email = shift;
- my ($message, $name);
- if ( UNIVERSAL::isa( $email => 'Email::Address' ) ) {
- ($email, $name) = ($email->address, $email->phrase);
- } else {
- ($email, $name) = RT::Interface::Email::ParseAddressFromHeader( $email );
- }
-
- $self->LoadByEmail( $email );
- $self->Load( $email ) unless $self->Id;
- $message = $self->loc('User loaded');
-
- unless( $self->Id ) {
- my $val;
- ($val, $message) = $self->Create(
- Name => $email,
- EmailAddress => $email,
- RealName => $name,
- Privileged => 0,
- Comments => 'Autocreated when added as a watcher',
- );
- unless ( $val ) {
- # Deal with the race condition of two account creations at once
- $self->LoadByEmail( $email );
- unless ( $self->Id ) {
- sleep 5;
- $self->LoadByEmail( $email );
- }
- if ( $self->Id ) {
- $RT::Logger->error("Recovered from creation failure due to race condition");
- $message = $self->loc("User loaded");
- }
- else {
- $RT::Logger->crit("Failed to create user ". $email .": " .$message);
+ my ($val, $message);
+
+ my ( $Address, $Name ) =
+ RT::Interface::Email::ParseAddressFromHeader($email);
+ $email = $Address;
+
+ $self->LoadByEmail($email);
+ $message = $self->loc('User loaded');
+ unless ($self->Id) {
+ $self->Load($email);
+ }
+ unless($self->Id) {
+ ( $val, $message ) = $self->Create(
+ Name => $email,
+ EmailAddress => $email,
+ RealName => $Name,
+ Privileged => 0,
+ Comments => 'Autocreated when added as a watcher');
+ unless ($val) {
+ # Deal with the race condition of two account creations at once
+ $self->LoadByEmail($email);
+ unless ($self->Id) {
+ sleep 5;
+ $self->LoadByEmail($email);
+ }
+ if ($self->Id) {
+ $RT::Logger->error("Recovered from creation failure due to race condition");
+ $message = $self->loc("User loaded");
+ }
+ else {
+ $RT::Logger->crit("Failed to create user ".$email .": " .$message);
+ }
}
}
+
+ if ($self->Id) {
+ return($self->Id, $message);
+ }
+ else {
+ return(0, $message);
+ }
+
+
}
- return (0, $message) unless $self->id;
- return ($self->Id, $message);
-}
+
+# }}}
+
+# {{{ sub ValidateEmailAddress
=head2 ValidateEmailAddress ADDRESS
@@ -531,86 +647,24 @@ sub ValidateEmailAddress {
# if the email address is null, it's always valid
return (1) if ( !$Value || $Value eq "" );
- if ( RT->Config->Get('ValidateUserEmailAddresses') ) {
- # We only allow one valid email address
- my @addresses = Email::Address->parse($Value);
- return ( 0, $self->loc('Invalid syntax for email address') ) unless ( ( scalar (@addresses) == 1 ) && ( $addresses[0]->address ) );
- }
-
-
my $TempUser = RT::User->new($RT::SystemUser);
$TempUser->LoadByEmail($Value);
- if ( $TempUser->id && ( !$self->id || $TempUser->id != $self->id ) )
+ if ( $TempUser->id && ( $TempUser->id != $self->id ) )
{ # if we found a user with that address
# it's invalid to set this user's address to it
- return ( 0, $self->loc('Email address in use') );
+ return (undef);
}
else { #it's a valid email address
return (1);
}
}
-=head2 SetEmailAddress
-
-Check to make sure someone else isn't using this email address already
-so that a better email address can be returned
-
-=cut
-
-sub SetEmailAddress {
- my $self = shift;
- my $Value = shift;
-
- my ($val, $message) = $self->ValidateEmailAddress( $Value );
- if ( $val ) {
- return $self->_Set( Field => 'EmailAddress', Value => $Value );
- } else {
- return ( 0, $message )
- }
-
-}
-
-=head2 EmailFrequency
-
-Takes optional Ticket argument in paramhash. Returns 'no email',
-'squelched', 'daily', 'weekly' or empty string depending on
-user preferences.
-
-=over 4
-
-=item 'no email' - user has no email, so can not recieve notifications.
+# }}}
-=item 'squelched' - returned only when Ticket argument is provided and
-notifications to the user has been supressed for this ticket.
+# {{{ sub CanonicalizeEmailAddress
-=item 'daily' - retruned when user recieve daily messages digest instead
-of immediate delivery.
-=item 'weekly' - previous, but weekly.
-
-=item empty string returned otherwise.
-
-=back
-
-=cut
-
-sub EmailFrequency {
- my $self = shift;
- my %args = (
- Ticket => undef,
- @_
- );
- return '' unless $self->id && $self->id != $RT::Nobody->id
- && $self->id != $RT::SystemUser->id;
- return 'no email' unless my $email = $self->EmailAddress;
- return 'squelched' if $args{'Ticket'} &&
- grep lc $email eq lc $_->Content, $args{'Ticket'}->SquelchMailTo;
- my $frequency = RT->Config->Get( 'EmailFrequency', $self ) || '';
- return 'daily' if $frequency =~ /daily/i;
- return 'weekly' if $frequency =~ /weekly/i;
- return '';
-}
=head2 CanonicalizeEmailAddress ADDRESS
@@ -628,14 +682,19 @@ sub CanonicalizeEmailAddress {
# Example: the following rule would treat all email
# coming from a subdomain as coming from second level domain
# foo.com
- if ( my $match = RT->Config->Get('CanonicalizeEmailAddressMatch') and
- my $replace = RT->Config->Get('CanonicalizeEmailAddressReplace') )
- {
- $email =~ s/$match/$replace/gi;
+ if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
+ $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
}
return ($email);
}
+
+# }}}
+
+# {{{ sub CanonicalizeUserInfo
+
+
+
=head2 CanonicalizeUserInfo HASH of ARGS
CanonicalizeUserInfo can convert all User->Create options.
@@ -656,9 +715,14 @@ sub CanonicalizeUserInfo {
}
-=head2 Password and authentication related functions
+# }}}
+
+
+# {{{ Password related functions
-=head3 SetRandomPassword
+# {{{ sub SetRandomPassword
+
+=head2 SetRandomPassword
Takes no arguments. Returns a status code and a new password or an error message.
If the status is 1, the second value returned is the new password.
@@ -674,8 +738,8 @@ sub SetRandomPassword {
}
- my $min = ( RT->Config->Get('MinimumPasswordLength') > 6 ? RT->Config->Get('MinimumPasswordLength') : 6);
- my $max = ( RT->Config->Get('MinimumPasswordLength') > 8 ? RT->Config->Get('MinimumPasswordLength') : 8);
+ my $min = ( $RT::MinimumPasswordLength > 6 ? $RT::MinimumPasswordLength : 6);
+ my $max = ( $RT::MinimumPasswordLength > 8 ? $RT::MinimumPasswordLength : 8);
my $pass = $self->GenerateRandomPassword( $min, $max) ;
@@ -691,7 +755,11 @@ sub SetRandomPassword {
}
-=head3 ResetPassword
+# }}}
+
+# {{{ sub ResetPassword
+
+=head2 ResetPassword
Returns status, [ERROR or new password]. Resets this user\'s password to
a randomly generated pronouncable password and emails them, using a
@@ -713,13 +781,38 @@ sub ResetPassword {
return ( 0, "$pass" );
}
- my $ret = RT::Interface::Email::SendEmailUsingTemplate(
- To => $self->EmailAddress,
- Template => 'PasswordChange',
- Arguments => {
- NewPassword => $pass,
- },
- );
+ my $template = RT::Template->new( $self->CurrentUser );
+
+ if ( $self->Privileged ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
+ }
+ else {
+ $template->LoadGlobalTemplate('RT_PasswordChange_NonPrivileged');
+ }
+
+ unless ( $template->Id ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange');
+ }
+
+ unless ( $template->Id ) {
+ $RT::Logger->crit( "$self tried to send "
+ . $self->Name
+ . " a password reminder "
+ . "but couldn't find a password change template" );
+ }
+
+ my $notification = RT::Action::SendPasswordEmail->new(
+ TemplateObj => $template,
+ Argument => $pass
+ );
+
+ $notification->SetHeader( 'To', $self->EmailAddress );
+
+ my ($ret);
+ $ret = $notification->Prepare();
+ if ($ret) {
+ $ret = $notification->Commit();
+ }
if ($ret) {
return ( 1, $self->loc('New password notification sent') );
@@ -730,7 +823,11 @@ sub ResetPassword {
}
-=head3 GenerateRandomPassword MIN_LEN and MAX_LEN
+# }}}
+
+# {{{ sub GenerateRandomPassword
+
+=head2 GenerateRandomPassword MIN_LEN and MAX_LEN
Returns a random password between MIN_LEN and MAX_LEN characters long.
@@ -916,7 +1013,11 @@ sub _GenerateRandomNextChar {
return ($i);
}
-=head3 SetPassword
+# }}}
+
+# {{{ sub SetPassword
+
+=head2 SetPassword
Takes a string. Checks the string's length and sets this user's password
to that string.
@@ -934,8 +1035,8 @@ sub SetPassword {
if ( !$password ) {
return ( 0, $self->loc("No password set") );
}
- elsif ( length($password) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [_1] characters long", RT->Config->Get('MinimumPasswordLength')) );
+ elsif ( length($password) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long", $RT::MinimumPasswordLength) );
}
else {
my $new = !$self->HasPassword;
@@ -952,7 +1053,7 @@ sub SetPassword {
}
-=head3 _GeneratePassword PASSWORD
+=head2 _GeneratePassword PASSWORD
returns an MD5 hash of the password passed in, in hexadecimal encoding.
@@ -968,7 +1069,7 @@ sub _GeneratePassword {
}
-=head3 _GeneratePasswordBase64 PASSWORD
+=head2 _GeneratePasswordBase64 PASSWORD
returns an MD5 hash of the password passed in, in base64 encoding
(obsoleted now).
@@ -985,12 +1086,16 @@ sub _GeneratePasswordBase64 {
}
-=head3 HasPassword
+# }}}
+
+
+=head2 HasPassword
Returns true if the user has a valid password, otherwise returns false.
=cut
+
sub HasPassword {
my $self = shift;
my $pwd = $self->__Value('Password');
@@ -1000,7 +1105,10 @@ sub HasPassword {
return 1;
}
-=head3 IsPassword
+
+# {{{ sub IsPassword
+
+=head2 IsPassword
Returns true if the passed in value is this user's password.
Returns undef otherwise.
@@ -1047,80 +1155,13 @@ sub IsPassword {
return (undef);
}
-=head3 AuthToken
+# }}}
-Returns an authentication string associated with the user. This
-string can be used to generate passwordless URLs to integrate
-RT with services and programms like callendar managers, rss
-readers and other.
-
-=cut
-
-sub AuthToken {
- my $self = shift;
- my $secret = $self->FirstAttribute("AuthToken");
- return $secret->Content if $secret;
-
- my $id = $self->id;
- $self = RT::User->new( $RT::SystemUser );
- $self->Load( $id );
- $secret = substr(Digest::MD5::md5_hex(time . {} . rand()),0,16);
- my ($status, $msg) = $self->SetAttribute( Name => "AuthToken", Content => $secret );
- unless ( $status ) {
- $RT::Logger->error( "Couldn't set auth token: $msg" );
- return undef;
- }
- return $secret;
-}
-
-=head3 GenerateAuthToken
-
-Generate a random authentication string for the user.
-
-=cut
-
-sub GenerateAuthToken {
- my $self = shift;
- my $token = substr(Digest::MD5::md5_hex(time . {} . rand()),0,16);
- return $self->SetAttribute( Name => "AuthToken", Content => $token );
-}
-
-=head3 GenerateAuthString
-
-Takes a string and returns back a hex hash string. Later you can use
-this pair to make sure it's generated by this user using L</ValidateAuthString>
-
-=cut
-
-sub GenerateAuthString {
- my $self = shift;
- my $protect = shift;
-
- my $str = $self->AuthToken . $protect;
- utf8::encode($str);
-
- return substr(Digest::MD5::md5_hex($str),0,16);
-}
+# }}}
-=head3 ValidateAuthString
+# {{{ sub SetDisabled
-Takes auth string and protected string. Returns true is protected string
-has been protected by user's L</AuthToken>. See also L</GenerateAuthString>.
-
-=cut
-
-sub ValidateAuthString {
- my $self = shift;
- my $auth_string = shift;
- my $protected = shift;
-
- my $str = $self->AuthToken . $protected;
- utf8::encode( $str );
-
- return $auth_string eq substr(Digest::MD5::md5_hex($str),0,16);
-}
-
-=head2 SetDisabled
+=head2 Sub SetDisabled
Toggles the user's disabled flag.
If this flag is
@@ -1129,69 +1170,53 @@ user will fail. The user will appear in no user listings.
=cut
+# }}}
+
sub SetDisabled {
my $self = shift;
- my $val = shift;
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
return (0, $self->loc('Permission Denied'));
}
-
- $RT::Handle->BeginTransaction();
- my $set_err = $self->PrincipalObj->SetDisabled($val);
- unless ($set_err) {
- $RT::Handle->Rollback();
- $RT::Logger->warning(sprintf("Couldn't %s user %s", ($val == 1) ? "disable" : "enable", $self->PrincipalObj->Id));
- return (undef);
- }
- $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" );
-
- $RT::Handle->Commit();
-
- if ( $val == 1 ) {
- return (1, $self->loc("User disabled"));
- } else {
- return (1, $self->loc("User enabled"));
- }
-
+ return $self->PrincipalObj->SetDisabled(@_);
}
-=head2 Disabled
-
-Returns true if user is disabled or false otherwise
-
-=cut
-
sub Disabled {
my $self = shift;
return $self->PrincipalObj->Disabled(@_);
}
+
+# {{{ Principal related routines
+
=head2 PrincipalObj
Returns the principal object for this user. returns an empty RT::Principal
if there's no principal object matching this user.
The response is cached. PrincipalObj should never ever change.
+=begin testing
+
+ok(my $u = RT::User->new($RT::SystemUser));
+ok($u->Load(1), "Loaded the first user");
+ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
+is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group");
+
+=end testing
+
=cut
+
sub PrincipalObj {
my $self = shift;
+ unless ($self->{'PrincipalObj'} &&
+ ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
+ ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
- unless ( $self->id ) {
- $RT::Logger->error("Couldn't get principal for not loaded object");
- return undef;
- }
-
- my $obj = RT::Principal->new( $self->CurrentUser );
- $obj->LoadById( $self->id );
- unless ( $obj->id ) {
- $RT::Logger->crit( 'No principal for user #'. $self->id );
- return undef;
- } elsif ( $obj->PrincipalType ne 'User' ) {
- $RT::Logger->crit( 'User #'. $self->id .' has principal of '. $obj->PrincipalType .' type' );
- return undef;
- }
- return $obj;
+ $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
+ $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
+ 'PrincipalType' => 'User') ;
+ }
+ return($self->{'PrincipalObj'});
}
@@ -1206,6 +1231,12 @@ sub PrincipalId {
return $self->Id;
}
+# }}}
+
+
+
+# {{{ sub HasGroupRight
+
=head2 HasGroupRight
Takes a paramhash which can contain
@@ -1236,11 +1267,14 @@ sub HasGroupRight {
$args{'GroupObj'}->Load( $args{'Group'} );
}
- # Validate and load up the GroupId
+ # {{{ Validate and load up the GroupId
unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
return undef;
}
+ # }}}
+
+
# Figure out whether a user has the right we're asking about.
my $retval = $self->HasRight(
Object => $args{'GroupObj'},
@@ -1248,8 +1282,14 @@ sub HasGroupRight {
);
return ($retval);
+
+
}
+# }}}
+
+# {{{ sub OwnGroups
+
=head2 OwnGroups
Returns a group collection object containing the groups of which this
@@ -1262,21 +1302,533 @@ sub OwnGroups {
my $groups = RT::Groups->new($self->CurrentUser);
$groups->LimitToUserDefinedGroups;
$groups->WithMember(PrincipalId => $self->Id,
- Recursively => 1);
+ Recursively => 1);
return $groups;
}
+# }}}
+
+# {{{ Links
+
+#much false laziness w/Ticket_Overlay.pm
+
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
+ MemberOf => { Base => 'MemberOf',
+ Target => 'HasMember', },
+ RefersTo => { Base => 'RefersTo',
+ Target => 'ReferredToBy', },
+ DependsOn => { Base => 'DependsOn',
+ Target => 'DependedOnBy', },
+ MergedInto => { Base => 'MergedInto',
+ Target => 'MergedInto', },
+
+);
+
+sub LINKDIRMAP { return \%LINKDIRMAP }
+
+#sub _Links {
+# my $self = shift;
+#
+# #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
+# #tobias meant by $f
+# my $field = shift;
+# my $type = shift || "";
+#
+# unless ( $self->{"$field$type"} ) {
+# $self->{"$field$type"} = new RT::Links( $self->CurrentUser );
+# if ( $self->CurrentUserHasRight('ShowTicket') ) {
+# # Maybe this ticket is a merged ticket
+# my $Tickets = new RT::Tickets( $self->CurrentUser );
+# # at least to myself
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $self->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# $Tickets->Limit( FIELD => 'EffectiveId',
+# VALUE => $self->EffectiveId );
+# while (my $Ticket = $Tickets->Next) {
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $Ticket->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# }
+# $self->{"$field$type"}->Limit( FIELD => 'Type',
+# VALUE => $type )
+# if ($type);
+# }
+# }
+# return ( $self->{"$field$type"} );
+#}
+
+=head2 DeleteLink
+
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
+
+=cut
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ #check acls
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
+
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
+
+ my ($direction, $remote_link);
+
+ if ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
+
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => 'DeleteLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0
+ );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
+}
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ return $self->_AddLink(%args);
+}
+
+#sub __GetTicketFromURI {
+# my $self = shift;
+# my %args = ( URI => '', @_ );
+#
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my $uri_obj = RT::URI->new( $self->CurrentUser );
+# $uri_obj->FromURI( $args{'URI'} );
+#
+# unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
+# my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+# $RT::Logger->warning( "$msg\n" );
+# return( 0, $msg );
+# }
+# my $obj = $uri_obj->Resolver->Object;
+# unless ( UNIVERSAL::isa($obj, 'RT::Ticket') && $obj->id ) {
+# return (1, 'Found not a ticket', undef);
+# }
+# return (1, 'Found ticket', $obj);
+#}
+
+=head2 _AddLink
+
+Private non-acled variant of AddLink so that links can be added during create.
+
+=cut
+
+sub _AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
+ return ($val, $msg) if !$val || $exist;
+
+ my ($direction, $remote_link);
+ if ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction = 'Base';
+ } elsif ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
+ }
+
+}
+
+
+
+# }}}
+
+
+# {{{ sub Rights testing
+
+=head1 Rights testing
+
+
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok($root->Id, "Found the root user");
+my $rootq = RT::Queue->new($root);
+$rootq->Load(1);
+ok($rootq->Id, "Loaded the first queue");
+
+ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
+
+my $new_user = RT::User->new($RT::SystemUser);
+my ($id, $msg) = $new_user->Create(Name => 'ACLTest'.$$);
+
+ok ($id, "Created a new user for acl test $msg");
+
+my $q = RT::Queue->new($new_user);
+$q->Load(1);
+ok($q->Id, "Loaded the first queue");
+
+
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
+ok ($gval, "Grant succeeded - $gmsg");
+
+
+ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
+ok ($gval, "Revocation succeeded - $gmsg");
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
+
+
+
+
+
+# Create a ticket in the queue
+my $new_tick = RT::Ticket->new($RT::SystemUser);
+my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
+ok($tickid, "Created ticket: $tickid");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+# Create a new group
+my $group = RT::Group->new($RT::SystemUser);
+$group->CreateUserDefinedGroup(Name => 'ACLTest'.$$);
+ok($group->Id, "Created a new group Ok");
+# Grant a group the right to modify tickets in a queue
+ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"Grant succeeed - $gm");
+# Add the user to the group
+ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
+ok ($aid, "Member added to group: $amsg");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
+
+
+# Remove the user from the group
+ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
+ok ($did,"Deleted the group member: $dmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+my $q_as_system = RT::Queue->new($RT::SystemUser);
+$q_as_system->Load(1);
+ok($q_as_system->Id, "Loaded the first queue");
+
+# Create a ticket in the queue
+my $new_tick2 = RT::Ticket->new($RT::SystemUser);
+my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
+ok($tick2id, "Created ticket: $tick2id");
+is($new_tick2->QueueObj->id, $q_as_system->Id, "Created a new ticket in queue 1");
+
+
+# make sure that the user can't do this without subgroup membership
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# Create a subgroup
+my $subgroup = RT::Group->new($RT::SystemUser);
+$subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest',$$);
+ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
+#Add the subgroup as a subgroup of the group
+my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
+ok ($said, "Added the subgroup as a member of the group");
+# Add the user to a subgroup of the group
+
+my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
+ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
+
+# {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
+
+my ($id, $msg);
+($id, $msg) = $group->SetDisabled(1);
+ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
+ ($id, $msg) = $group->SetDisabled(0);
+ok($id,$msg);
+# Test what happens when we disable the group the user is a member of directly
+
+($id, $msg) = $subgroup->SetDisabled(1);
+ ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
+ ($id, $msg) = $subgroup->SetDisabled(0);
+ ok ($id,$msg);
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
+
+# }}}
+
+
+my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
+ok($usrid,"removed the user from the group - $usrmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+#revoke the right to modify tickets in a queue
+ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"revoke succeeed - $gm");
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $q_as_system, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $q_as_system, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
+
+# Before we start Make sure the user does not have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Make sure the user can't modify the ticket before they're added as a watcher
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+
+# Grant "privileged users" the system right to create users
+# Create a privileged user.
+# have that user create another user
+# Revoke the right for privileged users to create users
+# have the privileged user try to create another user and fail the ACL check
+
+=end testing
+
+=cut
+
+# }}}
+
+
+# {{{ sub HasRight
+
=head2 HasRight
-Shim around PrincipalObj->HasRight. See L<RT::Principal>.
+Shim around PrincipalObj->HasRight. See RT::Principal
=cut
sub HasRight {
+
my $self = shift;
return $self->PrincipalObj->HasRight(@_);
}
+# }}}
+
+# {{{ sub CurrentUserCanModify
+
=head2 CurrentUserCanModify RIGHT
If the user has rights for this object, either because
@@ -1314,6 +1866,10 @@ sub CurrentUserCanModify {
}
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
=head2 CurrentUserHasRight
Takes a single argument. returns 1 if $Self->CurrentUser
@@ -1331,17 +1887,19 @@ sub CurrentUserHasRight {
sub _PrefName {
my $name = shift;
if (ref $name) {
- $name = ref($name).'-'.$name->Id;
+ $name = ref ($name).'-'.$name->Id;
}
return 'Pref-'.$name;
}
+# {{{ sub Preferences
+
=head2 Preferences NAME/OBJ DEFAULT
-Obtain user preferences associated with given object or name.
-Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
-override the entries with user preferences.
+ Obtain user preferences associated with given object or name.
+ Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
+ override the entries with user preferences.
=cut
@@ -1350,49 +1908,53 @@ sub Preferences {
my $name = _PrefName (shift);
my $default = shift;
- my $attr = RT::Attribute->new( $self->CurrentUser );
- $attr->LoadByNameAndObject( Object => $self, Name => $name );
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
my $content = $attr->Id ? $attr->Content : undef;
- unless ( ref $content eq 'HASH' ) {
- return defined $content ? $content : $default;
+ if (ref ($content) eq 'HASH') {
+ if (ref ($default) eq 'HASH') {
+ for (keys %$default) {
+ exists $content->{$_} or $content->{$_} = $default->{$_};
+ }
+ }
+ elsif (defined $default) {
+ $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ }
+ return $content;
}
-
- if (ref $default eq 'HASH') {
- for (keys %$default) {
- exists $content->{$_} or $content->{$_} = $default->{$_};
- }
- }
- elsif (defined $default) {
- $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ else {
+ return defined $content ? $content : $default;
}
- return $content;
}
+# }}}
+
+# {{{ sub SetPreferences
+
=head2 SetPreferences NAME/OBJ VALUE
-Set user preferences associated with given object or name.
+ Set user preferences associated with given object or name.
=cut
sub SetPreferences {
- my $self = shift;
- my $name = _PrefName( shift );
+ my $self = shift;
+ my $name = _PrefName (shift);
my $value = shift;
-
- return (0, $self->loc("No permission to set preferences"))
- unless $self->CurrentUserCanModify('Preferences');
-
- my $attr = RT::Attribute->new( $self->CurrentUser );
- $attr->LoadByNameAndObject( Object => $self, Name => $name );
- if ( $attr->Id ) {
- return $attr->SetContent( $value );
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
+ if ($attr->Id) {
+ return $attr->SetContent ($value);
}
else {
- return $self->AddAttribute( Name => $name, Content => $value );
+ return $self->AddAttribute ( Name => $name, Content => $value );
}
}
+# }}}
+
+
=head2 WatchedQueues ROLE_LIST
Returns a RT::Queues object containing every queue watched by the user.
@@ -1462,6 +2024,9 @@ sub WatchedQueues {
}
+
+# {{{ sub _CleanupInvalidDelegations
+
=head2 _CleanupInvalidDelegations { InsideTransaction => undef }
Revokes all ACE entries delegated by this user which are inconsistent
@@ -1484,59 +2049,63 @@ and logs an internal error if the deletion fails (should not happen).
sub _CleanupInvalidDelegations {
my $self = shift;
my %args = ( InsideTransaction => undef,
- @_ );
+ @_ );
unless ( $self->Id ) {
- $RT::Logger->warning("User not loaded.");
- return (undef);
+ $RT::Logger->warning("User not loaded.");
+ return (undef);
}
my $in_trans = $args{InsideTransaction};
return(1) if ($self->HasRight(Right => 'DelegateRights',
- Object => $RT::System));
+ Object => $RT::System));
# Look up all delegation rights currently posessed by this user.
my $deleg_acl = RT::ACL->new($RT::SystemUser);
$deleg_acl->LimitToPrincipal(Type => 'User',
- Id => $self->PrincipalId,
- IncludeGroupMembership => 1);
+ Id => $self->PrincipalId,
+ IncludeGroupMembership => 1);
$deleg_acl->Limit( FIELD => 'RightName',
- OPERATOR => '=',
- VALUE => 'DelegateRights' );
+ OPERATOR => '=',
+ VALUE => 'DelegateRights' );
my @allowed_deleg_objects = map {$_->Object()}
- @{$deleg_acl->ItemsArrayRef()};
+ @{$deleg_acl->ItemsArrayRef()};
# Look up all rights delegated by this principal which are
# inconsistent with the allowed delegation objects.
my $acl_to_del = RT::ACL->new($RT::SystemUser);
$acl_to_del->DelegatedBy(Id => $self->Id);
foreach (@allowed_deleg_objects) {
- $acl_to_del->LimitNotObject($_);
+ $acl_to_del->LimitNotObject($_);
}
# Delete all disallowed delegations
while ( my $ace = $acl_to_del->Next() ) {
- my $ret = $ace->_Delete(InsideTransaction => 1);
- unless ($ret) {
- $RT::Handle->Rollback() unless $in_trans;
- $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
- return (undef);
- }
+ my $ret = $ace->_Delete(InsideTransaction => 1);
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
+ return (undef);
+ }
}
$RT::Handle->Commit() unless $in_trans;
return (1);
}
+# }}}
+
+# {{{ sub _Set
+
sub _Set {
my $self = shift;
my %args = (
Field => undef,
Value => undef,
- TransactionType => 'Set',
- RecordTransaction => 1,
+ TransactionType => 'Set',
+ RecordTransaction => 1,
@_
);
@@ -1553,7 +2122,7 @@ sub _Set {
my $Old = $self->SUPER::_Value("$args{'Field'}");
my ($ret, $msg) = $self->SUPER::_Set( Field => $args{'Field'},
- Value => $args{'Value'} );
+ Value => $args{'Value'} );
#If we can't actually set the field to the value, don't record
# a transaction. instead, get out of here.
@@ -1575,6 +2144,10 @@ sub _Set {
}
}
+# }}}
+
+# {{{ sub _Value
+
=head2 _Value
Takes the name of a table column.
@@ -1602,7 +2175,7 @@ sub _Value {
#If the user wants to see their own values, let them
# TODO figure ouyt a better way to deal with this
- elsif ( defined($self->Id) && $self->CurrentUser->Id == $self->Id ) {
+ elsif ( $self->CurrentUser->Id == $self->Id ) {
return ( $self->SUPER::_Value($field) );
}
@@ -1616,96 +2189,14 @@ sub _Value {
}
-=head2 FriendlyName
-
-Return the friendly name
-
-=cut
-
-sub FriendlyName {
- my $self = shift;
- return $self->RealName if defined($self->RealName);
- return $self->Name if defined($self->Name);
- return "";
-}
-
-=head2 PreferredKey
-
-Returns the preferred key of the user. If none is set, then this will query
-GPG and set the preferred key to the maximally trusted key found (and then
-return it). Returns C<undef> if no preferred key can be found.
-
-=cut
-
-sub PreferredKey
-{
- my $self = shift;
- return undef unless RT->Config->Get('GnuPG')->{'Enable'};
- my $prefkey = $self->FirstAttribute('PreferredKey');
- return $prefkey->Content if $prefkey;
-
- # we don't have a preferred key for this user, so now we must query GPG
- require RT::Crypt::GnuPG;
- my %res = RT::Crypt::GnuPG::GetKeysForEncryption($self->EmailAddress);
- return undef unless defined $res{'info'};
- my @keys = @{ $res{'info'} };
- return undef if @keys == 0;
-
- if (@keys == 1) {
- $prefkey = $keys[0]->{'Fingerprint'};
- }
- else {
- # prefer the maximally trusted key
- @keys = sort { $b->{'TrustLevel'} <=> $a->{'TrustLevel'} } @keys;
- $prefkey = $keys[0]->{'Fingerprint'};
- }
-
- $self->SetAttribute(Name => 'PreferredKey', Content => $prefkey);
- return $prefkey;
-}
-
-sub PrivateKey {
- my $self = shift;
-
- my $key = $self->FirstAttribute('PrivateKey') or return undef;
- return $key->Content;
-}
-
-sub SetPrivateKey {
- my $self = shift;
- my $key = shift;
- # XXX: ACL
- unless ( $key ) {
- my ($status, $msg) = $self->DeleteAttribute('PrivateKey');
- unless ( $status ) {
- $RT::Logger->error( "Couldn't delete attribute: $msg" );
- return ($status, $self->loc("Couldn't unset private key"));
- }
- return ($status, $self->loc("Unset private key"));
- }
-
- # check that it's really private key
- {
- my %tmp = RT::Crypt::GnuPG::GetKeysForSigning( $key );
- return (0, $self->loc("No such key or it's not suitable for signing"))
- if $tmp{'exit_code'} || !$tmp{'info'};
- }
-
- my ($status, $msg) = $self->SetAttribute(
- Name => 'PrivateKey',
- Content => $key,
- );
- return ($status, $self->loc("Couldn't set private key"))
- unless $status;
- return ($status, $self->loc("Unset private key"));
-}
+# }}}
sub BasicColumns {
(
- [ Name => 'User Id' ],
- [ EmailAddress => 'Email' ],
- [ RealName => 'Name' ],
- [ Organization => 'Organization' ],
+ [ Name => 'User Id' ],
+ [ EmailAddress => 'Email' ],
+ [ RealName => 'Name' ],
+ [ Organization => 'Organization' ],
);
}