Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bestpractical/rt.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Vincent <jesse@bestpractical.com>2000-08-02 00:20:03 +0400
committerJesse Vincent <jesse@bestpractical.com>2000-08-02 00:20:03 +0400
commitaf25cb3f1c461c3670cb2b1b772e50d147062949 (patch)
treec6038113fad003de07e017c5ee5abab811640192
parent602ba96f23930ee22c8f47959f8926533b5dd5e5 (diff)
Weekend of 1 Aug 2000rt-1.3.10
--------------------- I spent the weekend in DC visiting family. This meant I got a bit of code written ;) Sadly, I have no access to the CVS server, so I'll be batching a bunch of commits. 1. Enabled CLI admin tool 2. Added ACL listing functionality to the CLI admin tool. 3. Enhanced RT::Queue->Grant such that it works with the structure of RT2 ACLs 4. Made the Logging framework actually log errors to STDERR. (This makes debugging the CLI tools much easier. It also means that the cli tools explain _why_ they're dying.) 5. Fully expunged use of Mysql's SQL keyword "now()". I'd have left this stuff in, except mysql doesn't seem to deal well with the idea that the entire world isn't one timezone. On top of that, it doesn't seem to have a way to force it into GMT mode that doesn't involve modifying init scripts. *sigh* 6. Did a whole bunch more work on the ACL checking in RT::User 7. Wrote up some preliminary docs on local hacks to RT 8. Added in a routine to allow local canonicalization of email addresses 9. Added in the concept of "Disabled users" To preserve RT2's database Integrity, whacking user accounts would be a bad thing. So, instead, we've got the concept of 'disabled' users. A disabled user fails ANY ACL check, ANY password check and doesn't appear in any lists of ACLs. (note that the lastmost statement isn't yet true) 10. rtadmin user -enable and rtadmin user -disable now work. 11. ACLs are now enforced for many ticket related actions. (this does mean that you'll want to insert some acls like those below) INSERT INTO ACL VALUES (1,0,'User','SuperUser','Queue',0); INSERT INTO ACL VALUES (2,3,'User','CreateTicket','Queue',0); INSERT INTO ACL VALUES (3,3,'User','ShowTicket','Ticket',0); INSERT INTO ACL VALUES (4,3,'User','ShowTicketHistory','Ticket',0); INSERT INTO ACL VALUES (6,3,'User','CreateTicket','Queue',1); INSERT INTO ACL VALUES (7,3,'User','ModifyTicket','Ticket',1); INSERT INTO ACL VALUES (8,1,'User','Superuser','System',0); INSERT INTO ACL VALUES (9,0,'Everyone','Superuser','System',0);
-rwxr-xr-xMakefile2
-rwxr-xr-xNEWS39
-rwxr-xr-xTODO21
-rwxr-xr-xetc/config.pm95
-rwxr-xr-xetc/schema.mysql12
-rwxr-xr-xlib/RT/ACE.pm118
-rwxr-xr-xlib/RT/ACL.pm15
-rwxr-xr-xlib/RT/CurrentUser.pm39
-rw-r--r--lib/RT/Date.pm9
-rwxr-xr-xlib/RT/Interface/Email.pm98
-rwxr-xr-xlib/RT/Queue.pm71
-rwxr-xr-xlib/RT/Record.pm11
-rwxr-xr-xlib/RT/Ticket.pm172
-rwxr-xr-xlib/RT/Tickets.pm19
-rwxr-xr-xlib/RT/User.pm285
-rwxr-xr-xlib/rt/ui/cli/admin.pm228
-rwxr-xr-xlib/rt/ui/cli/manipulate.pm56
17 files changed, 892 insertions, 398 deletions
diff --git a/Makefile b/Makefile
index 7c61fb9b3b..263862af34 100755
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ RTGROUP = rt
RT_VERSION_MAJOR = 1
RT_VERSION_MINOR = 3
-RT_VERSION_PATCH = 9
+RT_VERSION_PATCH = 10
RT_VERSION = $(RT_VERSION_MAJOR).$(RT_VERSION_MINOR).$(RT_VERSION_PATCH)
TAG = rt-$(RT_VERSION_MAJOR)-$(RT_VERSION_MINOR)-$(RT_VERSION_PATCH)
diff --git a/NEWS b/NEWS
index d1818322b3..ffb8ac5fc0 100755
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,42 @@
+Weekend of 1 Aug 2000
+---------------------
+I'm in DC this weekend hacking. Sadly, I have no access to the CVS server,
+so I'll be batching a bunch of commits.
+
+1. Enabled CLI admin tool
+2. Added ACL listing functionality to the CLI admin tool.
+3. Enhanced RT::Queue->Grant such that it works with the structure of RT2
+ ACLs
+4. Made the Logging framework actually log errors to STDERR.
+ (This makes debugging the CLI tools much easier. It also means
+ that the cli tools explain _why_ they're dying.)
+5. Fully expunged use of Mysql's SQL keyword "now()". I'd have left this
+ stuff in, except mysql doesn't seem to deal well with the idea that the
+ entire world isn't one timezone. On top of that, it doesn't seem to
+ have a way to force it into GMT mode that doesn't involve modifying init
+ scripts. *sigh*
+6. Did a whole bunch more work on the ACL checking in RT::User
+7. Wrote up some preliminary docs on local hacks to RT
+8. Added in a routine to allow local canonicalization of email addresses
+9. Added in the concept of "Disabled users" To preserve RT2's database
+ Integrity, whacking user accounts would be a bad thing. So, instead,
+ we've got the concept of 'disabled' users. A disabled user fails ANY
+ ACL check, ANY password check and doesn't appear in any lists of ACLs.
+ (note that the lastmost statement isn't yet true)
+
+10. rtadmin user -enable and rtadmin user -disable now work.
+11. ACLs are now enforced for many ticket related actions.
+ (this does mean that you'll want to insert some acls like those below)
+
+INSERT INTO ACL VALUES (1,0,'User','SuperUser','Queue',0);
+INSERT INTO ACL VALUES (2,3,'User','CreateTicket','Queue',0);
+INSERT INTO ACL VALUES (3,3,'User','ShowTicket','Ticket',0);
+INSERT INTO ACL VALUES (4,3,'User','ShowTicketHistory','Ticket',0);
+INSERT INTO ACL VALUES (6,3,'User','CreateTicket','Queue',1);
+INSERT INTO ACL VALUES (7,3,'User','ModifyTicket','Ticket',1);
+INSERT INTO ACL VALUES (8,1,'User','Superuser','System',0);
+INSERT INTO ACL VALUES (9,0,'Everyone','Superuser','System',0);
+
1 Mar 2000
----------
diff --git a/TODO b/TODO
index a4d4e6b439..6e7f147ad6 100755
--- a/TODO
+++ b/TODO
@@ -12,30 +12,9 @@ an RT1 queue at a remote box. This is Tobix' list as of May 11 2000:
- probably a lot of work with the GUI, particularly handling links
-- date handling
- user information + user prioritying.
-Locally, we often have users with different emails. We need to keep track
-of them, also when they change emails. We need to store comments about
-them. Most important; we must be able to rate them! Good customers
-should get a higher priority than ordinary requestors, and those
-should get a higher priority than those who constantly bother us with
-stupid FAQs.
-
-I'd daresay that this will not be included in the official RT 2.0 release,
-but it will be available as a user contributed patch, and it will be
-included in post-2.0 releases (from now on; contr).
-
-- Admin tools & ACL.
-
-We don't need it locally for some weeks, at least - I handle it through
-SQL as for now. Anyway, it must come before the official RT 2.0 release.
-
-- Groups
-
-This is essensial for us if an admin tool should be useful. contr.
-
- "Smart" Next/Prev-links
This is a GUI detail, which probably will have to wait until post-2.0.
diff --git a/etc/config.pm b/etc/config.pm
index d8a3b474bd..57df2b4bc1 100755
--- a/etc/config.pm
+++ b/etc/config.pm
@@ -10,7 +10,57 @@ package RT;
# $web_auth_mechanism $web_auth_cookies_allow_no_path $DefaultLocale
# $LocalePath $Nobody $Logger/;
+
+
+# {{{ Base Configuration (everything below should be set by the Makefile)
+
+# name of RT installation
+# Use a smart name, it's not smart changing this, unless you know
+# exactly what you're doing.
+$rtname="!!RT_MAIL_TAG!!";
+
+# Domain name and hostname
+$domain="!!RT_DOMAIN!!";
+$host="!!RT_HOST!!";
+
+# $passwd_min defines the minimum length for user passwords.
+$user_passwd_min = "!!RT_USER_PASSWD_MIN!!";
+
+
+# }}}
+
+# {{{ Mail Gateway configuration
+
+# If $BouncesToRTOwner is defined, RT will send mail that it believes
+# might be a bounce to $RT::OwnerEmail
+$BouncesToRTOwner = 1;
+
+
+# If $StoreBounces is defined, RT will record messages that it believes
+# might be autogenerated and might lead to mail loops.
+# As it does this, it will try to be careful not to send mail to the
+# sender of these messages
+$StoreBounces = undef;
+
+
+#
+#
+# This subroutine converts email addresses into canonical form.
+# it takes one email address in and returns the proper canonical
+# form. You can dump whatever your proper local config is in here
+sub CanonicalizeAddress {
+ my $email = shift;
+ # Example: the following rule would treat all email
+ # coming from a subdomain as coming from second level domain
+ # foo.com
+ #$email =~ s/\@(.*).foo.com/\@foo.com/;
+ return ($email)
+}
+# }}}
+
+
# {{{ Logging
+
# Logging. The default is to log anything except debugging
# information to a logfile. Check the Log::Dispatch POD for
# information about how to get things by syslog, mail or anything
@@ -37,6 +87,7 @@ package RT;
use Log::Dispatch 1.2;
use Log::Dispatch::File;
+use Log::Dispatch::Screen;
$Logger=Log::Dispatch->new();
$Logger->add(Log::Dispatch::File->new
@@ -45,8 +96,12 @@ $Logger->add(Log::Dispatch::File->new
filename=>'!!RT_LOGFILE!!',
mode=>'append',
callback => sub {my %p=@_; return "$p{message}\n"}
- ));
-
+ ));
+$Logger->add(Log::Dispatch::Screen->new
+ ( name => 'screen',
+ min_level => 'err',
+ stderr => 1
+ ));
# }}}
# {{{ Options for the webui
@@ -140,21 +195,7 @@ $Logger->add(Log::Dispatch::File->new
# }}}
-# {{{ Base Configuration (everything below should be set by the Makefile)
-
-# name of RT installation
-# Use a smart name, it's not smart changing this, unless you know
-# exactly what you're doing.
-$rtname="!!RT_MAIL_TAG!!";
-
-# Domain name and hostname
-$domain="!!RT_DOMAIN!!";
-$host="!!RT_HOST!!";
-
-# $passwd_min defines the minimum length for user passwords.
-$user_passwd_min = "!!RT_USER_PASSWD_MIN!!";
-# }}}
# {{{ Database Configuration
@@ -178,7 +219,7 @@ $DatabaseType="!!DB_TYPE!!";
# }}}
-# {{{ Mail configuration
+# {{{ Mail sending configuration
#$MailAlias is a generic alias to send mail to for any request
#already in a queue because of the nature of RT, mail sent to any
@@ -197,11 +238,6 @@ $CommentAddress="!!RT_COMMENT_MAIL_ALIAS!!";
## TODO: Mail::Internet might need some configuration.
-## This might and might not be the right place to put signatures.
-## Alternatives are ~/.signature and in the DB (only DB supported at
-## the moment).
-#%Signatures=(joe=>"This is joe's signature");
-
## Places to search for the signature:
#@Signature=('$home/.signature','$home/.signature.rt','/etc/rt/signatures/$userid');
@@ -215,15 +251,6 @@ $WebrtImagePath = "!!WEB_IMAGE_PATH!!";
$WebPath = "!!WEB_PATH!!";
$WebURL = "http://$host\.$domain/$WebPath/";
-
-# WEB_AUTH_MECHANISM defines what sort of authentication you'd like to use
-# for the web ui. Valid choices are: "cookies" and "external". Cookies
-# uses http cookies to keep track of authentication. External means that
-# you will have configured your web server to prompt for the user's
-# credentials and authenticate them before RT ever sees the request.
-
-$web_auth_mechanism = "!!WEB_AUTH_MECHANISM!!";
-
# }}}
# {{{ Localization configuration
@@ -241,8 +268,14 @@ $LocalePath = "!!LOCALE_PATH!!";
# {{{ No User servicable parts inside
#
+#RT's "nobody user" is a genuine database user. its ID lives here.
$Nobody=2;
+
+
+#TODO document this
$SIG{__WARN__} = sub {$RT::Logger->log(level=>'warning',message=>$_[0])};
+
+#When we call die, trap it and log->crit with the value of the die.
$SIG{__DIE__} = sub {
if ($^S) {
$RT::Logger->log(level=>'warning',message=>"died during an eval: $_[0]");
diff --git a/etc/schema.mysql b/etc/schema.mysql
index 0d9a897d88..f8fce82782 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -150,7 +150,9 @@ CREATE TABLE Users (
EmailAddress VARCHAR(120),
FreeformContactInfo BLOB,
Organization VARCHAR(200),
-
+ Disabled INT, # Disabled users can't get granted ACLs and doesn't show up
+ # in user lists.
+
CanManipulate INT, #Whether this user can be granted rights
#If 0, the user can only be a requestor
# }}}
@@ -242,14 +244,12 @@ CREATE TABLE GroupMembers (
# {{{ Table ACL {
-# TODO: Comments or documentation needed here :)
-
CREATE TABLE ACL (
id int primary key auto_increment,
PrincipalId INT,
- PrincipalType VARCHAR(16),
- Right VARCHAR(16),
- Scope VARCHAR(16),
+ PrincipalType VARCHAR(25),
+ Right VARCHAR(25),
+ Scope VARCHAR(25),
AppliesTo INT
);
diff --git a/lib/RT/ACE.pm b/lib/RT/ACE.pm
index fd03430327..a1b54542ac 100755
--- a/lib/RT/ACE.pm
+++ b/lib/RT/ACE.pm
@@ -4,6 +4,57 @@ package RT::ACE;
use RT::Record;
@ISA= qw(RT::Record);
+use vars qw (%SCOPE
+ %QUEUERIGHTS
+ %TICKETRIGHTS
+ %SYSTEMRIGHTS
+ %METAPRINCIPALS);
+
+%SCOPE = ( System => 'System-level right',
+ Queue => 'Queue-level right'
+ );
+
+# Queue rights are the sort of queue rights that can only be granted
+# to real people or groups
+%QUEUERIGHTS = ( See => 'Can this principal see this queue',
+ List => 'Display a listing of ticket',
+ ModifyWatchers => 'Modify the queue watchers',
+ ModifyACL => 'Modify this queue\'s ACL',
+ CreateTicket => 'Create a ticket in this queue'
+ );
+
+# System rights are rights granted to the whole system
+%SYSTEMRIGHTS = ( CreateQueue => 'Create queues',
+ DeleteQueue => 'Delete queues',
+ AdminUsers => 'Create, Delete and Modify users',
+ ModifySelf => 'Modify one\'s own RT account',
+ ModifySystemACL => 'Modify system ACLs'
+ );
+
+#Ticket rights are the sort of queue rights that can be granted to
+#principals and metaprincipals
+
+%TICKETRIGHTS = ( ShowTicket => 'Show ticket summary',
+ ShowTicketHistory => 'Show ticket history',
+ ShowTicketComments => 'Show ticket private commentary',
+ CorrespondOnTicket => 'Reply to ticket',
+ CommentOnTicket => 'Comment on ticket',
+ OwnTicket => 'Own a ticket',
+ ModifyTicket => 'Modify ticket',
+ DeleteTicket => 'Delete ticket'
+ );
+
+%TICKET_METAPRINCIPALS = ( Owner => 'The owner of a ticket',
+ Requestor => 'The requestor of a ticket',
+ Cc => 'The CC of a ticket',
+ AdminCc => 'The administrative CC of a ticket',
+ );
+
+%GLOBAL_METAPRINCIPALS = ( Everyone => 'Any valid RT principal' );
+
+
+
+
# {{{ sub new
sub new {
my $proto = shift;
@@ -17,6 +68,7 @@ sub new {
# }}}
# {{{ sub _Create
+
sub _Create {
my $self = shift;
my %args = ( PrincipalId => undef,
@@ -31,30 +83,34 @@ return (1, 'Granted');
# }}}
# {{{ sub GrantQueueRight
-sub GrantQueueRight {
- my $self = shift;
- my $args = ( Scope => 'Queue',
- @_);
-
- if ($args->{'Scope'} ne 'Queue') {
- return (0, 'Scope must be queue for queue rights');
- }
-
- #unless $self->CurrentUser->id has 'ModifyQueueACL' for (queue == $args->{'AppliesTo'})
- {
+sub GrantQueueRight {
+
+ my $self = shift;
+ my %args = ( PrincipalType => undef,
+ PrincipalId => undef,
+ @_);
+
+
+ if ($args->{'Scope'} ne 'Queue') {
+ return (0, 'Scope must be queue for queue rights');
+ }
+
+
+ #unless $self->CurrentUser->id has 'ModifyQueueACL' for (queue == $args->{'AppliesTo'}) {
# if the user can't do it, return a (0, 'No permission to grant rights');
- }
-
+ #}
+
return ($self->_Create($args));
}
# }}}
# {{{ sub GrantGlobalQueueRight
+
sub GrantGlobalQueueRight {
my $self = shift;
- my $args = ( AppliesTo => 0,
+ my %args = ( AppliesTo => 0,
@_);
return ($self->GrantQueueRight($args));
@@ -63,15 +119,18 @@ sub GrantGlobalQueueRight {
# }}}
# {{{ sub GrantSystemRight
+
sub GrantSystemRight {
my $self = shift;
- my $args = (Scope => 'System',
+ my %args = (Scope => 'System',
AppliesTo => 0,
@_);
#If the user can't grant system rights,
- # return (0, 'No permission to grant rights');
-
+ unless ($self->CurrentUser->HasRight('ModifySystemACL')) {
+
+ return (0, 'No permission to grant rights');
+ }
return ($self->_Create( $args ));
}
@@ -91,10 +150,12 @@ sub _Accessible {
}
# }}}
+# {{{ sub _Set
sub _Set {
my $self = shift;
return (0, "ACEs can only be created and deleted.");
}
+# }}}
1;
__DATA__
@@ -106,10 +167,22 @@ __DATA__
PrincipalType, PrincipalId, Right,Scope,AppliesTo
-=head1 Valid Scopes
+=head1 Scopes
+
+Scope is the scope of the right granted, not the granularity of the grant.
+For example, Queue and Ticket rights are both granted for a "queue."
+Rights with a scope of "Ticket" are rights that act on a single existing ticket.The 'AppliesTo' for a right with the scope of 'Ticket' is the Queue that the
+right applies to. The 'AppliesTo' for a right with the scope of 'Queue' is the
+Queue that the right applies to. Rights with a scope of 'System' don't have an
+AppliesTo. (They're global).
+Rights with a scope of "Queue" are rights that act on a queue.
+Rights with a scope of "System" are rights that act on some other aspect
+of the system.
+
- Queue
- System
+=item Ticket
+=item Queue
+=item System
=head1 Rights
@@ -211,6 +284,10 @@ Modify Users
Name: ModifyUser
Principals: <user> <group>
+Modify Self
+ Name: ModifySelf
+ Principals: <user> <group>
+
Browse Users
Name: BrowseUsers (NOT IMPLEMENTED in 2.0)
@@ -238,3 +315,4 @@ Modify System ACL
TicketAdminCc,NULL
Everyone,NULL
+=cut
diff --git a/lib/RT/ACL.pm b/lib/RT/ACL.pm
index 1c701b361c..477238d1d0 100755
--- a/lib/RT/ACL.pm
+++ b/lib/RT/ACL.pm
@@ -3,8 +3,8 @@
# Copyright (c) 2000 Jesse Vincent <jesse@fsck.com>
package RT::ACL;
-use DBIx::EasySearch;
-@ISA= qw(DBIx::EasySearch);
+use RT::EasySearch;
+@ISA= qw(RT::EasySearch);
# {{{ sub new
sub new {
@@ -86,7 +86,7 @@ sub LimitScopeToQueue {
Limit the ACL to global queue rights. (Rights granted across all queues)
=cut
-sub LimitScopeToQueue {
+sub LimitScopeToAllQueues {
my $self = shift;
$self->Limit( FIELD =>'Scope',
@@ -104,7 +104,7 @@ Limit the ACL to system rights
=cut
-sub LimitScopeToQueue {
+sub LimitScopeToSystem {
my $self = shift;
$self->Limit( FIELD =>'Scope',
@@ -119,7 +119,7 @@ $right is a right listed in perldoc RT::ACE
=cut
-sub LimitScopeToQueue {
+sub LimitRightTo {
my $self = shift;
my $right = shift;
@@ -188,9 +188,8 @@ sub LimitPrincipalsToType {
my $self=shift;
my $type=shift;
$self->Limit(ENTRYAGGREGATOR => 'OR',
- FIELD => 'PrincipalType',
- VALUE => $type );
+ FIELD => 'PrincipalType',
+ VALUE => $type );
}
-
1;
diff --git a/lib/RT/CurrentUser.pm b/lib/RT/CurrentUser.pm
index fe6592f96f..d38e67ceeb 100755
--- a/lib/RT/CurrentUser.pm
+++ b/lib/RT/CurrentUser.pm
@@ -103,6 +103,45 @@ sub IsPassword {
}
# }}}
+# {{{ Convenient ACL methods
+
+=head2 HasTicketRight
+
+calls $self->UserObj->HasTicketRight with the arguments passed in
+
+=cut
+
+sub HasTicketRight {
+ my $self = shift;
+ return ($self->UserObj->HasTicketRight(@_));
+}
+
+
+
+
+=head2 HasQueueRight
+
+calls $self->UserObj->HasQueueRight with the arguments passed in
+
+=cut
+
+sub HasQueueRight {
+ my $self = shift;
+ return ($self->UserObj->HasQueueRight(@_));
+}
+
+=head2 HasSystemRight
+
+calls $self->UserObj->HasSystemRight with the arguments passed in
+
+=cut
+
+
+sub HasSystemRight {
+ my $self = shift;
+ return ($self->UserObj->HasSystemRight(@_));
+}
+# }}}
# {{{ sub HasRight
sub HasRight {
diff --git a/lib/RT/Date.pm b/lib/RT/Date.pm
index 5cf2491dc0..dca158cdb1 100644
--- a/lib/RT/Date.pm
+++ b/lib/RT/Date.pm
@@ -66,7 +66,7 @@ sub Set {
$self->{'time'} = timegm($sec,$min,$hours,$mday,$mon,$year);
}
else {
- die "Couldn't parse date format for ".$args{'Value'}."\n";
+ $RT::Logger->debug( "Couldn't parse date $args{'Value'} as a $args{'Format'}");
}
}
@@ -79,6 +79,13 @@ sub Set {
# }}}
+
+# {{{ sub SetToNow
+sub SetToNow {
+ my $self = shift;
+ return($self->Set(Format => 'unix', Value => time))
+}
+# }}}
# {{{ sub Diff
=head2 Diff
diff --git a/lib/RT/Interface/Email.pm b/lib/RT/Interface/Email.pm
index 96b83e398d..d77430ced5 100755
--- a/lib/RT/Interface/Email.pm
+++ b/lib/RT/Interface/Email.pm
@@ -4,28 +4,52 @@
package RT::Interface::Email;
use RT::Ticket;
-
+use MIME::Parser;
+use Mail::Address;
+use RT::User;
+use RT::CurrentUser;
+
# {{{ sub activate
sub activate {
+ my ($Verbose, ReturnTid, $Debug);
+
+ #Set some sensible defaults
+ my $Queue = 1;
+ my $Action = 'Correspond';
+
+ while (my $flag = shift @ARGV) {
+ if (($flag eq '-v') or ($flag eq '--verbose')) {
+ $Verbose = 1;
+ }
+ if (($flag eq '-t') or ($flag eq '--ticketid')) {
+ $ReturnTid = 1;
+ }
+
+ if (($flag eq '-d') or ($flag eq '--debug')) {
+ $Debug = 1;
+ }
+
+ if (($flag eq '-q') or ($flag eq '--queue')) {
+ $Queue = shift @ARGV;
+ }
+ if (($flag eq '-a') or ($flag eq '--action')) {
+ $Action = shift @ARGV;
+ }
+ if (($flag eq '-r') or ($flag eq '--area')) {
+ $area = shift @ARGV;
+ }
+
- my $Queue = $ARGV[0];
- my $Action = $ARGV[1];
- my $Area = $ARGV[2];
my ($From, $TicketId, $Subject,$SquelchReplies);
- if (!defined ($Queue)) { $Queue = "general";}
- if (!defined ($Action)) { $Action = "correspond";}
-
- $RT::Logger->log(message=>"RT Mailgate started for $Queue/$Action", level=>'info');
+ $RT::Logger->info("RT Mailgate started for $Queue/$Action");
my $time = time;
# {{{ Parse the MIME entity
# Create a new parser object:
- use MIME::Parser;
- use Mail::Address;
my $parser = new MIME::Parser;
@@ -35,19 +59,15 @@ sub activate {
## need to put each msg as an in-core scalar before saving it to
## the database, don't we?
+ ## At the same time, we should make sure that we nuke attachments
+ ## Over max size and return them
+
## TODO: Remove the temp dir when we don't need it any more.
my $AttachmentDir = "/tmp/rt-tmp-$time-$$-".int(rand(100));
- ## TODO: Will die and bounce if the disk goes full or if there are
- ## other reasons why we couldn't create this dir. Is that right?
- ## Can we do it better? Tobix thinks that any caring RT
- ## administrator should set up config.pm to send an email or maybe
- ## even a mail to a pager whenever RT dies, is this a good enough
- ## assumption, or should we also log it as an emergency? Or even
- ## explicitly send a mail to RT-Owner? It's also a bit flawed to
- ## die here, as the temp directory is only needed for the biggest
- ## attachments.
+ #TODO log an emergency and bounce the message (MIME Encoded)
+ # to the sender and to RT-Owner if we can't store the message
mkdir("$AttachmentDir", 0700) || die "Couldn't create temp dir!";
# Set up output directory for files:
@@ -58,6 +78,7 @@ sub activate {
# If content length is <= 20000 bytes, store each msg as in-core scalar;
# Else, write to a disk file (the default action):
+
$parser->output_to_core(20000);
# }}} (temporary directory)
@@ -78,8 +99,9 @@ sub activate {
#Get us a current user object.
my $CurrentUser = &GetCurrentUser($head);
-
-
+
+ my $MessageId = $head->get->('Message-Id');
+
# {{{ Lets check for mail loops of various sorts.
my $IsAutoGenerated = &CheckForAutoGenerated($head);
@@ -102,14 +124,15 @@ sub activate {
# should only stop _this_ transaction from generating emails.
# A "silent transaction" mode - yeah, that was also a
# suggested feature at RTCon. That will be enough from
- # stopping loops. TODO: I also think it's important that it's
+ # stopping loops.
+ # TODO: I also think it's important that it's
# clearly written in the ticket history that this is a "silent
# transaction"
}
#If it's actually a _local loop_ we want to warn someone
if ($IsALoop) {
- $RT::Logger->crit("RT Recieved mail from itself");
+ $RT::Logger->crit("RT Recieved mail ($MessageId) from itself");
#Should we mail it to RTOwner?
## TODO: This should be documented in config.pm
@@ -167,7 +190,7 @@ sub activate {
else {
#TODO Return an error message
- $RT::Logger->crit("$Action aliases require a TicketId to work on (from $CurrentUser->address");
+ $RT::Logger->crit("$Action aliases require a TicketId to work on (from $CurrentUser->address) $MessageId");
return();
}
}
@@ -178,7 +201,9 @@ sub activate {
if ($Action =~ /comment/i){
my $Ticket = new RT::Ticket($CurrentUser);
$Ticket->Load($TicketId) || die "Could not load ticket";
- $Ticket->Open; # Reopening it, if necessary
+ #TODO: make this a scrip
+ $Ticket->Open; # Reopening it, if necessary
+
# TODO: better error handling
$Ticket->Comment(MIMEObj=>$entity);
}
@@ -196,7 +221,7 @@ sub activate {
}
elsif ($Action ne 'action') {
- $RT::Logger->crit("$Action type unknown. check definitions in /etc/aliases");
+ $RT::Logger->crit("$Action type unknown for $MessageId check definitions in /etc/aliases");
}
}
@@ -309,7 +334,7 @@ sub ParseCommands {
for (@commands) {
next if /^$/;
chomp;
- $RT::Logger->log(message=>"Action requested through email: $_", level=>'info');
+ $RT::Logger->info("Action requested through email: $_");
my ($command, $arguments)=/^(?:\s*)((?:\w|-)+)(?: (.*))?$/
or die "syntax error ($_)";
if ($command =~ /^(Un)?[Ll]ink$/) {
@@ -328,22 +353,23 @@ sub ParseCommands {
warn $TicketId;
}
if (!$TicketId) {
- die "Links can only be done at tickets";
+ die "Links require a base and a target ticket";
}
my $Ticket = new RT::Ticket($CurrentUser);
$Ticket->Load($TicketId) || die "Could not load ticket";
- # dirty? yes. how to fix?
+
+ #TODO: use a published interface. +++
$Ticket->_NewLink(dir=>$dir,Target=>$to,Base=>$from,Type=>$typ);
- $RT::Logger->log(level=>'info',
- message=>$CurrentUser->UserId." did a linking action by mail ($_)");
+ $RT::Logger->info( "$CurrentUser->UserId." created a link by mail ($_)");
} else {
die "unknown command $command : $_";
}
}
}
+
# }}}
-# {{{ sub MailSender
+# {{{ sub MailError
sub MailError {
my %args = (To => undef,
From =>undef,
@@ -379,7 +405,12 @@ sub GetCurrentUser {
#Lets take the from and load a user object.
- use RT::CurrentUser;
+
+ my $Address = $FromObj->address;
+
+ #This will apply local address canonicalization rules
+ $Address = &RT::CanonicalizeAddres($Address);
+
my $CurrentUser = RT::CurrentUser->new($FromObj->address);
# One more try if we couldn't find that user
@@ -388,7 +419,6 @@ sub GetCurrentUser {
unless ($CurrentUser->Id) {
#If it fails, create a user
- use RT::User;
my $SystemUser = new RT::CurrentUser(1);
my $NewUser = RT::User->new($SystemUser);#Create a user as root
#TODO: Figure out a better way to do this
diff --git a/lib/RT/Queue.pm b/lib/RT/Queue.pm
index 5a33174d46..a2a7e8560b 100755
--- a/lib/RT/Queue.pm
+++ b/lib/RT/Queue.pm
@@ -212,22 +212,15 @@ sub Watchers {
#
-sub Grant {
- my $self = shift;
- my $principal_type = shift;
- my $principal_id = shift;
- my $right = shift;
-
-}
#returns an EasySearch of ACEs everyone who has anything to do with this queue.
# {{{ sub ACL
sub ACL {
my $self = shift;
if (!$self->{'acl'}) {
- $self->{'acl'} = RT::ACL->new($self->{'self'});
- $user->{'acl'}->Limit(FIELD => 'queue',
- VALUE => "$self->id");
+ use RT::ACL;
+ $self->{'acl'} = new RT::ACL;
+ $self->{'acl'}->LimitScopeToQueue($self->Id);
}
return ($self->{'acl'});
@@ -240,31 +233,59 @@ sub ACL {
#
#ACCESS CONTROL
+# {{{ sub CurrentUserHasRight
sub CurrentUserHasRight {
my $self = shift;
- return 1;
-}
-sub HasRight {
- my $self = shift;
my $right = shift;
- return (1);
+ return ($self->HasRight( Principal=> $self->CurrentUser,
+ Right => "$right"));
- # by default, the actor is the current user
- if (!@_) {
- my $actor = $self->CurrentUser->Id();
- }
- else {
- my $actor = shift;
- }
-
+}
-
-
+# }}}
+
+# {{{ sub HasRight
+
+# TAKES: Right and optional "Actor" which defaults to the current user
+sub HasRight {
+ my $self = shift;
+ my %args = ( Right => undef,
+ Principal => undef,
+ @_);
+ unless(defined $args{'Principal'}) {
+ $RT::Logger->warn("Principal attrib undefined for Queue::HasRight");
+ }
+ return($args{'Principal'}->HasQueueRight(QueueObj => $self,
+ Right => $args{'Right'}));
}
+=head2 sub Grant
+
+Grant is a convenience method for creating a new ACE in the ACL.
+It passes off its values along with a scope and applies to of
+the current object.
+Grant takes a param hash of the following fields PrincipalType, PrincipalId and Right.
+
+=cut
+
+sub Grant {
+ my $self = shift;
+ my %args = ( PrincipalType => 'User',
+ PrincipalId => undef,
+ Right => undef,
+ @_
+ );
+ use RT::ACE;
+ my $ACE = new RT::ACE;
+ return($ACE->Create(PrincipalType => $args{'PrinicpalType'},
+ PrincipalId => $args{'PrincipalId'},
+ Right => $args{'Right'},
+ Scope => 'Queue',
+ AppliesTo => $self->Id ));
+}
#
# {{{ sub CreatePermitted
diff --git a/lib/RT/Record.pm b/lib/RT/Record.pm
index 680632d475..d984265406 100755
--- a/lib/RT/Record.pm
+++ b/lib/RT/Record.pm
@@ -54,11 +54,14 @@ sub _Handle {
# {{{ sub Create
sub Create {
my $self = shift;
+ if ($self->_Accessible('Created', 'auto')) {
+ my $now = new RT::Date;
+ $now->Set(Format=> 'unix', Value => time);
+ push @_, 'Created', $now->ISO();
+ }
push @_, 'Creator', $self->{'user'}->id
if $self->_Accessible('Creator', 'auto');
- # print STDERR "In RT::Record->create\n";
my $id = $self->SUPER::Create(@_);
- # print STDERR "RT::Record->create Loading by Ref $id\n";
return($id);
}
@@ -142,8 +145,8 @@ sub _Set {
my $self = shift;
my $field = shift;
#if the user is trying to modify the record
-
- $self->SUPER::_Set('LastUpdatedBy', $self->{'user'}->id)
+ $RT::Logger->debug("in RT::Record::Set for $self ".$self->Id ."\n");
+ $self->SUPER::_Set('LastUpdatedBy', $self->CurrentUser->id)
if ($self->_Accessible('LastUpdatedBy','auto'));
$self->SUPER::_Set($field, @_);
diff --git a/lib/RT/Ticket.pm b/lib/RT/Ticket.pm
index 5464cae874..dd8b10e705 100755
--- a/lib/RT/Ticket.pm
+++ b/lib/RT/Ticket.pm
@@ -2,8 +2,8 @@
# (c) 1996-2000 Jesse Vincent <jesse@fsck.com>
# This software is redistributable under the terms of the GNU GPL
#
-
package RT::Ticket;
+use RT::User;
use RT::Record;
use RT::Link;
use RT::Links;
@@ -60,7 +60,7 @@ Arguments: ARGS is a hash of named parameters. Valid parameters are:
id
EffectiveId
- Queue
+ Queue - Either a Queue object or a QueueId
QueueTag
Requestor -- A requestor object, if available. Eventually taken from the MIME object.
RequestorEmail -- the requestors email. Eventually taken from Requestor or the MIME object
@@ -83,9 +83,8 @@ Returns: TICKETID, Transaction Object, Error Message
sub Create {
my $self = shift;
- my $ErrStr;
+ my ( $ErrStr, $Queue);
-
my %args = (id => undef,
EffectiveId => undef,
Queue => undef,
@@ -102,28 +101,33 @@ sub Create {
TimeWorked => 0,
Due => undef,
MIMEObj => undef,
-
@_);
- unless ($self->CurrentUserHasRight('CreateTicket')) {
- return (0, "Permission Denied");
- }
-
#TODO Load queue defaults
- if (!$args{'Queue'} && $args{'QueueTag'}) {
- my $q=RT::Queue->new($self->CurrentUser);
- $q->LoadByCol("QueueId", $args{'QueueTag'});
- $args{'Queue'}=$q->id;
+
+ if ( (defined($args{'Queue'})) && (!ref($args{'Queue'})) ) {
+ $Queue=RT::Queue->new($self->CurrentUser);
+ $Queue->Load($args{'Queue'});
+ #TODO error check this and return 0 if it's not loading properly
}
-
+ elsif (ref($args{'Queue'}) eq 'RT::Queue') {
+ $Queue = $args{'Queue'};
+ }
+ else {
+ $RT::Logger->err($args{'Queue'} . " not a recognised queue object.");
+ }
#Can't create a ticket without a queue.
- unless ($args{'Queue'}) {
- $RT::Logger->log(level=>'warning',
- message=>"No queue given for ticket create request '".$args{'Subject'}."'");
+ unless (defined ($Queue)) {
+ $RT::Logger->err( "No queue given for ticket create request '".$args{'Subject'}."'");
return (0, 0,'Queue not set');
}
+
+ unless ($Queue->CurrentUserHasRight('CreateTicket')) {
+ return (0,0,"Permission Denied");
+ }
+
#TODO we should see what sort of due date we're getting, rather
# than assuming it's in ISO format.
my $due = new RT::Date;
@@ -132,7 +136,7 @@ sub Create {
my $id = $self->SUPER::Create(Id => $args{'id'},
EffectiveId => $args{'EffectiveId'},
- Queue => $args{'Queue'},
+ Queue => $Queue->Id,
Alias => $args{'Alias'},
Owner => $args{'Owner'} || $RT::Nobody,
Subject => $args{'Subject'},
@@ -151,8 +155,7 @@ sub Create {
#Now that we know the self
(my $error, my $message) = $self->SUPER::_Set("EffectiveId",$id);
if ($error == 0) {
- $RT::Logger->log(level=>'warning',
- message=>"Couldn't set EffectiveId for Ticket $id: $message.");
+ $RT::Logger->warning("Couldn't set EffectiveId for Ticket $id: $message.");
return (0, 0, $message);
}
@@ -310,7 +313,8 @@ sub AddAdminCc {
my $self = shift;
return ($self->AddWatcher ( Type => 'AdminCc', @_));
}
-# }}}1
+
+# }}}
# }}}
@@ -705,9 +709,6 @@ sub Queue {
# {{{ Date printing routines
-# Created and LastUpdated belongs to the DBIx::Record layer (and maybe even deeper)
-
-
# {{{ sub DueAsString
sub DueAsString {
@@ -725,8 +726,7 @@ sub GraceTimeAsString {
if ($self->Due) {
my $now=new RT::Date;
- $now->Set(Format => 'unix',
- Value => time);
+ $now->SetToNow();
return($now->DiffAsString($self->DueObj));
} else {
return "";
@@ -771,8 +771,8 @@ sub LongSinceToldAsString {
my $self = shift;
if ($self->Told) {
-
my $now = new RT::Date;
+ $now->SetToNow();
return $now->DiffAsString($self->ToldObj);
} else {
return "Never";
@@ -792,8 +792,6 @@ sub ToldAsString {
}
# }}}
-# }}}
-
# {{{ sub LastUpdatedByObj
sub LastUpdatedByObj {
my $self=shift;
@@ -823,7 +821,6 @@ sub TimeWorkedAsString {
# }}}
-
# {{{ Routines dealing with correspondence/comments
# {{{ sub Comment
@@ -1189,29 +1186,28 @@ sub SetOwner {
my $more_params={TransactionType=>$Type};
my ($NewOwnerObj);
- require RT::User;
+ $RT::Logger->debug("in RT::Ticket->SetOwner()");
+
$NewOwnerObj = RT::User->new($self->CurrentUser);
my $OldOwnerObj = $self->Owner;
if (!$NewOwnerObj->Load($NewOwner)) {
-
- return (0, "That user does not exist");
+ return (0, "That user does not exist");
}
-
#If thie ticket has an owner and it's not the current user
- # TODO: check this
-
- if ($Type ne 'Steal' and
- $self->Owner->Id != $RT::Nobody and
- $self->CurrentUser->Id ne $self->Owner->Id()) {
-
+ if (($Type ne 'Steal' ) and #If we're not stealing
+ ($self->Owner->Id != $RT::Nobody ) and #and the owner is set
+ ($self->CurrentUser->Id ne $self->Owner->Id())) { #and it's not us
return(0, "You can only reassign tickets that you own or that are unowned");
}
+
#If we've specified a new owner and that user can't modify the ticket
- elsif (($NewOwner) and (!$self->HasRight('OwnTickets',$NewOwnerObj->Id))) {
- return (0, "That user may not own requests in that queue")
+ elsif (($NewOwnerObj) and (!$NewOwnerObj->HasTicketRight(Right => 'OwnTicket',
+ TicketObj => $self,
+ ))) {
+ return (0, "That user may not own requests in that queue");
}
@@ -1222,17 +1218,7 @@ sub SetOwner {
}
- # elsif ( #TODO $new_owner doesn't have queue perms ) {
- # return (0,"That user doesn't have permission to modify this request");
- # }
-
- else {
- #TODO
- #If we're giving the request to someone other than $self->CurrentUser
- #send them mail
- }
-
- my ($trans,$msg)=$self->_Set('Owner',$NewOwnerObj->Id,0,$more_params);
+ my ($trans,$msg)=$self->_Set('Owner',$NewOwnerObj->Id,0,$more_params);
return ($trans,
($trans
? ("Owner changed from ".$OldOwnerObj->UserId." to ".$NewOwnerObj->UserId)
@@ -1244,11 +1230,12 @@ sub SetOwner {
# {{{ sub Take
sub Take {
my $self = shift;
+ $RT::Logger->debug("in RT::Ticket->Take()");
my ($trans,$msg)=$self->SetOwner($self->CurrentUser->Id, 'Take');
- return ($trans,
- $trans
- ? "Ticket taken"
- : $msg);
+ if ($trans == 0) {
+ return (0, $msg);
+ }
+ return ($trans, $msg);
}
# }}}
@@ -1286,8 +1273,6 @@ sub Steal {
# }}}
-
-
# }}}
# {{{ Routines dealing with status
@@ -1347,7 +1332,6 @@ sub Resolve {
# }}}
# }}}
-
# {{{ sub UpdateTold and _UpdateTold
# UpdateTold - updates the told and makes a transaction
@@ -1355,7 +1339,11 @@ sub Resolve {
sub UpdateTold {
my $self=shift;
my $timetaken=shift || 0;
- return($self->_Set('Told','now()',$timetaken,{TransactionType=>'Told',IsSQL=>1}));
+ my $now = new RT::Date;
+ $now->SetToNow();
+ #TODO: Update _Set's syntax. we need to deal with the ugly format.
+ return($self->_Set('Told',$now->ISO,$timetaken,
+ {TransactionType=>'Told'}));
}
# _UpdateTold - updates the told without the transaction, that's
@@ -1363,7 +1351,9 @@ sub UpdateTold {
sub _UpdateTold {
my $self=shift;
- return($self->SUPER::_Set('Told','now()',1));
+ my $now = new RT::Date;
+ $now->SetToNow();
+ return($self->SUPER::_Set('Told',$now->ISO,1));
}
# }}}
@@ -1468,6 +1458,7 @@ sub _Accessible {
sub _Set {
my $self = shift;
+ $RT::Logger->debug("now in _Set\n");
unless ($self->CurrentUserHasRight('ModifyTicket')) {
return (0, "Permission Denied");
}
@@ -1520,12 +1511,13 @@ sub _Value {
my $self = shift;
my $field = shift;
- my ($package, $filename, $line) = caller;
- unless ($self->CurrentUserHasRight('ShowTicket')) {
+
+ #If the current user doesn't have ACLs, don't let em at it.
+
+ unless ($self->CurrentUserHasRight('ShowTicket')) {
return (0, "Permission Denied");
}
-
return($self->SUPER::_Value($field));
}
@@ -1541,7 +1533,7 @@ sub _UpdateTimeTaken {
my $self = shift;
my $Minutes = shift;
my ($Total);
-
+
$Total = $self->_Value("TimeWorked");
$Total = ($Total || 0) + ($Minutes || 0);
$self->SUPER::_Set("TimeWorked", $Total);
@@ -1565,9 +1557,10 @@ sub _UpdateDateActed {
sub CurrentUserHasRight {
my $self = shift;
my $right = shift;
-# TODO this is is a stub
- return 1;
- return ($self->HasRight("$right",$self->CurrentUser->Id()));
+
+ return ($self->HasRight( Principal=> $self->CurrentUser,
+ Right => "$right"));
+
}
# }}}
@@ -1577,39 +1570,26 @@ sub CurrentUserHasRight {
# TAKES: Right and optional "Actor" which defaults to the current user
sub HasRight {
my $self = shift;
- my $right = shift;
- my $actor = shift;
-
- my $RightClause = "(Right = '$right') OR (Right = 'SuperUser')";
- my $ScopeClause = "(Scope = 'Queue') AND ((AppliesTo = ".$self->Queue->id().") OR (AppliesTo = 0))";
- my $PrincipalsClause = "(PrincipalType = 'User') AND ((PrincipalId = $actor) OR (PrincipalId = 0))";
-
+ my %args = ( Right => undef,
+ Principal => undef,
+ @_);
+ unless(defined $args{'Principal'}) {
+ croak;
+ #$RT::Logger->warn("Principal attrib undefined for Ticket::HasRight");
+ }
+ return($args{'Principal'}->HasTicketRight(TicketObj => $self,
+ Right => $args{'Right'}));
+
+
+ #TODO this needs to move into User.pm's 'hasTicketRight'
+
$PrincipalsClause .= " OR (PrincipalType = 'Owner') " if ($actor == $self->Owner->Id);
$PrincipalsClause .= "OR (PrincipalType = 'TicketRequestor') " if ($self->IsRequestor($actor));
$PrincipalsClause .= "OR (PrincipalType = 'TicketCc') " if ($self->IsCc($actor));
$PrincipalsClause .= "OR (PrincipalType = 'TicketAdminCc') " if ($self->IsAdminCc($actor));
-
- $GroupPrincipalsClause = "((PrincipalType = 'Group') AND (PrincipalId = GroupMembers.Id) AND (GroupMembers.UserId = $actor))";
-
- my $query_string_1 = "SELECT COUNT(ACL.id) FROM ACL, GroupMembers WHERE (($ScopeClause) AND ($RightClause) AND ($GroupPrincipalsClause))";
-
- my $query_string_2 = "SELECT COUNT(ACL.id) FROM ACL WHERE (($ScopeClause) AND ($RightClause) AND ($PrincipalsClause))";
-
- my ($hitcount);
-
- $hitcount = $self->{'DBIxHandle'}->FetchResult($query_string_1);
-
- #if there's a match, the right is granted
- return (1) if ($hitcount);
-
- $hitcount = $self->{'DBIxHandle'}->FetchResult($query_string_2);
- return (1) if ($hitcount);
-
- return(0);
- }
+}
# }}}
-# }}}
1;
diff --git a/lib/RT/Tickets.pm b/lib/RT/Tickets.pm
index 468df9930f..6ab9473652 100755
--- a/lib/RT/Tickets.pm
+++ b/lib/RT/Tickets.pm
@@ -37,6 +37,25 @@ sub NewItem {
}
# }}}
+# {{{ sub Next
+sub Next {
+ my $self = shift;
+ my $Ticket = $self->SUPER::Next();
+ if ((defined($Ticket)) &&
+ ($Ticket->CurrentUserHasRight('ShowTicket'))) {
+ return($Ticket);
+ }
+ #If the user doesn't have the right to show this ticket
+ elsif (defined($Ticket)) {
+ return($self->Next());
+ }
+ #if there never was any ticket
+ else {
+ return(undef);
+ }
+
+}
+# }}}
# {{{ sub Owner
sub Owner {
my $self = shift;
diff --git a/lib/RT/User.pm b/lib/RT/User.pm
index cb6960e2a7..2e1856d24f 100755
--- a/lib/RT/User.pm
+++ b/lib/RT/User.pm
@@ -1,9 +1,8 @@
# $Header$
-# (c) 1996-1999 Jesse Vincent <jesse@fsck.com>
+# (c) 1996-2000 Jesse Vincent <jesse@fsck.com>
# This software is redistributable under the terms of the GNU GPL
#
-
package RT::User;
use RT::Record;
@ISA= qw(RT::Record);
@@ -33,6 +32,8 @@ sub _Accessible {
EmailAddress => 'read/write',
FreeformContactInfo => 'read/write',
Organization => 'read/write',
+ Disabled => 'read', #To modify this attribute, we have helper
+ #methods
CanManipulate => 'read/write',
# }}}
@@ -127,6 +128,7 @@ sub Create {
# }}}
# {{{ sub Delete
+
sub Delete {
my $self = shift;
@@ -164,6 +166,7 @@ sub Delete {
}
}
+
# }}}
# {{{ sub Load
@@ -196,6 +199,11 @@ sub LoadByEmail {
sub IsPassword {
my $self = shift;
my $value = shift;
+
+ if ($self->Disabled) {
+ $RT::Logger->info("Disabled user ".$self->UserId." tried to log in");
+ return(undef);
+ }
if ($value == $self->_Value('Password')) {
return (1);
}
@@ -205,80 +213,245 @@ sub IsPassword {
}
# }}}
-# {{{ sub DisplayPermitted
-sub DisplayPermitted {
- my $self = shift;
- #TODO: Implement
- return(1);
-}
-# }}}
-
-# {{{ sub ModifyPermitted
-sub ModifyPermitted {
- my $self = shift;
- #TODO: Implement
- return(1);
-}
-# }}}
-
# {{{ sub Signature
+
sub Signature {
my $self=shift;
return ($self->SUPER::Signature);
## TODO: The stuff below might be a nice feature, but since we don't need it
## at the moment, it's left out.
-
+
if (0) {
- my @entry=getpwnam($self->Gecos || $self->UserId);
- my $home=$entry[7];
-## TODO: Check if the commented out line might work better
-# for my $trythis (@RT::signature) {
- for my $trythis ("$home/.signature", "$home/pc/sign.txt", "$home/pc/sign") {
- if (-r $trythis) {
- local($/);
- undef $/;
- open(SIGNATURE, "<$trythis");
- $signature=<SIGNATURE>;
- close(SIGNATURE);
- return $signature;
- }
- }
- return undef;
+ my @entry=getpwnam($self->Gecos || $self->UserId);
+ my $home=$entry[7];
+ ## TODO: Check if the commented out line might work better
+ # for my $trythis (@RT::signature) {
+ for my $trythis ("$home/.signature", "$home/pc/sign.txt", "$home/pc/sign") {
+ if (-r $trythis) {
+ local($/);
+ undef $/;
+ open(SIGNATURE, "<$trythis");
+ $signature=<SIGNATURE>;
+ close(SIGNATURE);
+ return $signature;
+ }
+ }
+ return undef;
}
}
# }}}
+# {{{ sub Disable
+
+=head2 Sub Disable
+
+Disable takes no arguments and returns 1 on success and undef on failure.
+It causes this user to have his/her disable flag set. If this flag is
+set, all password checks for this user will fail. All ACL checks for this
+user will fail.
+
+=cut
+
+sub Disable {
+ my $self = shift;
+ if ($self->CurrentUser->HasSystemRight('AdminUsers')) {
+ return($self->_Set('Disabled',1));
+ }
+}
+# }}}
+
+# {{{ sub Enable
+
+=head2 Sub Enable
+
+Disable takes no arguments and returns 1 on success and undef on failure.
+It causes this user to have his/her disable flag unset. see sub Disable
+for a fuller treatment of this
+
+=cut
+
+sub Enable {
+ my $self = shift;
+
+ if ($self->CurrentUser->HasSystemRight('AdminUsers')) {
+ return($self->_Set('Disabled',0));
+}
+}
+
+# }}}
+# {{{ sub HasQueueRight
+=head2 HasQueueRight( QueueObj => RT::Queue, Right => 'Right' )
+
+Returns 1 if this user has the right specified in the paramhash. for the queue
+passed in.
+
+Returns undef if they don't
+
+=cut
+
+sub HasQueueRight {
+ my $self = shift;
+ my %args = ( QueueObj => undef,
+ Right => undef,
+ @_);
+
+ unless (ref ($args{'QueueObj'}) =~ /^RT::Queue/) {
+ $RT::Logger->debug("RT::User::HasQueueRight was passed $args{'QueueObj'} as a queue object");
+ }
+
+ return ($self->_HasRight(Scope => 'Queue',
+ AppliesTo => $args{'QueueObj'}->Id,
+ Right => "$args{'Right'}"));
+
+}
+
+# }}}
+
+
+# {{{ sub HasTicketRight
+
+=head2 HasTicketRight( TicketObj => RT::Ticket, Right => 'Right' )
+
+Returns 1 if this user has the ticket right specified for the ticket object
+passed in.
+
+Returns undef if they don't
+
+=cut
+
+sub HasTicketRight {
+ my $self = shift;
+ my %args = ( TicketObj => undef,
+ Right => undef,
+ @_);
+
+ #Check to make sure that the ticketobj is really a ticketobject
+ unless (ref ($args{'TicketObj'}) =~ /^RT::Ticket/) {
+ $RT::Logger->debug("RT::User::HasTicketRight was passed $args{'TicketObj'} as a ticket object. It's type is ".ref($args{'TicketObj'})."\n ");
+ }
+
+ return ($self->_HasRight(Scope => 'Ticket',
+ AppliesTo => $args{'TicketObj'}->Queue->Id,
+ Right => "$args{'Right'}"));
+
+}
+
+# }}}
# {{{ sub HasSystemRight
+
+=head2 HasSystemRight ( Right => 'right')
+
+Returns 1 if this user has the right 'right'
+
+Returns undef if this user doesn't
+
+=cut
+
sub HasSystemRight {
- my $self = shift;
- my $right = shift;
+ my $self = shift;
+ my %args = ( Right => 'undef',
+ @_);
+
+ return ($self->_HasRight ( Scope => 'System',
+ Right => $args{'Right'}));
+
+}
+
+# }}}
+
+# {{{ sub _HasRight
- my $RightClause = "(Right = '$right') OR (Right = 'SuperUser')";
- my $ScopeClause = "(Scope = 'System')";
- my $PrincipalsClause = "(PrincipalType = 'User') AND ((PrincipalId = $actor) OR (PrincipalId = 0))";
+=head2 sub _HasRight (Right => 'right', Scope => 'scope', AppliesTo => int,
+ ExtendedPrincipals => SQL)
+
+_HasRight is a private helper method for checking a user's rights. It takes
+several options:
+
+=item Right is a textual right name
+
+=item Scope is a textual scope name. (As of July these were Queue, Ticket and System
+
+=item AppliesTo is the numerical Id of the object identified in the scope. For tickets, this is the queue #. for queues, this is the queue #
+
+=item ExtendedPrincipals is an SQL select clause which assumes that the only
+table in play is ACL. It's used by HasTicketRight to pass in which
+metaprincipals apply
+
+Returns 1 if a matching ACE was found.
+
+Returns undef if no ACE was found.
+
+=cut
+
+
+sub _HasRight {
+
+ my $self = shift;
+ my %args = ( Right => undef,
+ Scope => undef,
+ AppliesTo => 0,
+ ExtendedPrincipals => undef,
+ @_);
+
+
+ if ($self->Disabled) {
+ $RT::Logger->debug ("Disabled User: ".$self->UserId." failed access check for ".$args{'Right'}." to object ".$args{'Scope'}."/".$args{'AppliesTo'}."\n");
+ return (undef);
+ }
+ my $RightClause = "(Right = '$args{'Right'}')";
+
+ my $ScopeClause = "(Scope = '$args{'Scope'}')";
+
+ #If an AppliesTo was passed in, we should pay attention to it.
+ #otherwise, none is needed
+
+ $ScopeClause = "($ScopeClause AND ((AppliesTo = 0) OR (AppliesTo = $args{'AppliesTo'})))"
+ if ($args{'AppliesTo'});
+
+
+ # The generic principals clause looks for users with my id
+ # and Rights that apply to _everyone_
+ my $PrincipalsClause = "(((PrincipalType = 'User') AND (PrincipalId = ".$self->Id.")) OR (PrincipalType = 'Everyone'))";
- $GroupPrincipalsClause = "((PrincipalType = 'Group') AND (PrincipalId = GroupMembers.Id) AND (GroupMembers.UserId = $actor))";
-
- my $query_string_1 = "SELECT COUNT(ACL.id) FROM ACL, GroupMembers WHERE (($ScopeClause) AND ($RightClause) AND ($GroupPrincipalsClause))";
-
- my $query_string_2 = "SELECT COUNT(ACL.id) FROM ACL WHERE (($ScopeClause) AND ($RightClause) AND ($PrincipalsClause))";
-
- my ($hitcount);
-
- $hitcount = $self->{'DBIxHandle'}->FetchResult($query_string_1);
-
- #if there's a match, the right is granted
- return (1) if ($hitcount);
-
- $hitcount = $self->{'DBIxHandle'}->FetchResult($query_string_2);
- return (1) if ($hitcount);
+ # If the user is the superuser, grant them the damn right ;)
+ my $SuperUserClause = "(Right = 'SuperUser') AND (Scope = 'System') AND (AppliesTo = 0)";
+
+ # If we've been passed in an extended principals clause, we should lump it
+ # on to the existing principals clause. it'll make life easier
+ if ($args{'ExtendedPrincipals'}) {
+ $PrincipalsClause = "(($PrincipalsClause) OR ($args{'ExtendedPrincipalsClause'}))";
+ }
+ my $GroupPrincipalsClause = "((PrincipalType = 'Group') AND (PrincipalId = GroupMembers.Id) AND (GroupMembers.UserId = ".$self->Id."))";
+
+
+ # This query checks to se whether the user has the right as a member of a group
+ my $query_string_1 = "SELECT COUNT(ACL.id) FROM ACL, GroupMembers WHERE (((($ScopeClause) AND ($RightClause)) OR ($SuperUserClause)) AND ($GroupPrincipalsClause))";
+
+ # This query checks to see whether the current user has the right directly
+ my $query_string_2 = "SELECT COUNT(ACL.id) FROM ACL WHERE (((($ScopeClause) AND ($RightClause)) OR ($SuperUserClause)) AND ($PrincipalsClause))";
+
+
+
+ my ($hitcount);
+# $RT::Logger->debug("Now Trying $query_string_1\n");
+ $hitcount = $self->{'DBIxHandle'}->FetchResult($query_string_1);
- return(0);
+ #if there's a match, the right is granted
+ return (1) if ($hitcount);
+# $RT::Logger->debug("No ACL matched $query_string_1\n");
+
+
+
+ $hitcount = $self->{'DBIxHandle'}->FetchResult($query_string_2);
+ return (1) if ($hitcount);
+
+ $RT::Logger->debug("No ACL matched $query_string_2\n") ;
+
+ return(0);
}
# }}}
-
1;
diff --git a/lib/rt/ui/cli/admin.pm b/lib/rt/ui/cli/admin.pm
index 66bb0c00db..9836b42cb9 100755
--- a/lib/rt/ui/cli/admin.pm
+++ b/lib/rt/ui/cli/admin.pm
@@ -3,12 +3,15 @@
# This software is redistributable under the terms of the GNU GPL
#
package rt::ui::cli::admin;
+use RT::User;
+use RT::Queue;
+
# {{{ sub activate
sub activate {
my ($current_user);
- require RT::CurrentUser;
+ use RT::CurrentUser;
($current_user,undef)=getpwuid($<);
$CurrentUser = new RT::CurrentUser($current_user);
if (!$CurrentUser) {
@@ -28,7 +31,19 @@ sub activate {
sub ParseArgs {
for ($i=0;$i<=$#ARGV;$i++) {
- if ($ARGV[$i] =~ 'q') {
+ if ($ARGV[$i] =~ /listacl/i) {
+ my $type = $ARGV[++$i];
+ if ($type =~ /us/i) {
+ my $user = $ARGV[++$i];
+ &UserACLList($user);
+ }
+ elsif ($type =~ /q/i) {
+ my $queue = $ARGV[++$i];
+ &QueueACLList($queue);
+ }
+ }
+
+ elsif ($ARGV[$i] =~ 'q') {
$action=$ARGV[++$i];
if ($action eq "-list") {
@@ -117,7 +132,17 @@ sub ParseArgs {
require RT::User;
$action=$ARGV[++$i];
-
+
+ if ($action =~ /-disable/) {
+ my $userid = $ARGV[++$i];
+ &DisableUser($userid);
+ }
+ if ($action =~ /-enable/) {
+ my $userid = $ARGV[++$i];
+ &EnableUser($userid);
+ }
+
+
if ($action eq "-modify") {
$user_id=$ARGV[++$i];
if (!$user_id) {
@@ -136,35 +161,6 @@ sub ParseArgs {
&cli_create_user($user_id);
}
- elsif ($action eq "-getpwent") {
- $passwd=$ARGV[++$i];
- $admin=$ARGV[++$i];
-
- print "Getpwent is not currently supported. A patch would be appreciated\n";
- exit(0);
-
- if (!defined($admin)) {
- print "Usage: user -getpwent <password> <administrator> [<users>...]\n";
- exit(0);
- }
- if (defined($ARGV[$i++])) {
- while (my $login=$ARGV[++$i]) {
- ($login, $domain) = split('@', $login);
- $domain || ($domain = $host);
- &add_pwent($domain, getpwnam($login), $CurrentUser);
- }
- } else {
- #Sometimes it really had been useful beeing able to combine while with
- #else..
-
- &setpwent;
- while (&add_pwent($host, getpwent, $CurrentUser))
- {;}
- &endpwent;
- }
-
- }
-
elsif ($action eq "-delete") {
$user_id=$ARGV[++$i];
if (!$user_id) {
@@ -176,12 +172,6 @@ sub ParseArgs {
}
}
- elsif ($ARGV[$i] =~ 'a') {
- $user_id=$ARGV[++$i];
- $queue_id=$ARGV[++$i];
- $privs=$ARGV[++$i];
- &cli_user_acl($user_id,$queue_id,$privs);
- }
else{
&cli_help_rt_admin();
@@ -192,26 +182,76 @@ sub ParseArgs {
# }}}
-# Add/Modify users by pwent:
-# This code by tobix...
-# {{{ sub add_pwent
-sub add_pwent {
- if (!@_) {return undef;}
- my ($domain, $name,$pass,$uid,$gid,
- $quota,$comment,$gcos,$dir,$shell,$whoami) = @_;
- my ($realname,$office,$phone)=split(/,/,$gcos);
-
- #TODO replace this with an object call
- # my ($result, $msg)=&rt::add_modify_user_info ($name,$realname,$passwd,"$name\@$domain",$phone,$office,$comment,
- # ($name eq $whoami ? 1 : $admin),$whoami);
-
- # Report to STDOUT:
- print "$msg\n" if ($msg);
-
- return $result;
+# {{{ sub DisableUser
+
+=head2 DisableUser
+
+This routine takes a userid as its argument and tries to disable it.
+If the user's already disabled, it tells the user. If it's not,
+it checks that the currentuser can modify user records. if so, it
+does what it needs
+
+=cut
+
+sub DisableUser {
+ my $userid = shift;
+ use RT::User;
+ my $User = new RT::User($CurrentUser);
+
+ #TODO: Error check the load
+ my $res = $User->Load($userid);
+ if (!$res) {
+ print "Couldn't load user $userid\n";
+ exit(-1);
+ }
+ unless ($CurrentUser->HasSystemRight('AdminUsers')) {
+ print "Permission denied";
+ }
+ my $result = $User->Disable();
+ if ($result) {
+ print "User ".$User->UserId."(".$User->Id.") disabled.\n";
+ return;
+ }
+ else {
+ print "There's been an error\n";
+ }
+}
+
+# }}}
+# {{{ sub EnableUser
+
+=head2 EnableUser
+
+This routine takes a userid as its argument and tries to enable it.
+
+=cut
+
+sub EnableUser {
+ my $userid = shift;
+ use RT::User;
+ my $User = new RT::User($CurrentUser);
+
+ #TODO: Error check the load
+ my $res = $User->Load($userid);
+ if (!$res) {
+ print "Couldn't load user $userid\n";
+ exit(-1);
+ }
+ unless ($CurrentUser->HasSystemRight('AdminUsers')) {
+ print "Permission denied";
+ exit(-1);
+ }
+ my $result = $User->Enable();
+ if ($result) {
+ print "User ".$User->UserId."(".$User->Id.") enabled.\n";
+ return;
+ }
+ else {
+ print "There's been an error\n";
+ }
}
-# }}}
+# }}}
# {{{ sub cli_modify_user
sub cli_modify_user {
@@ -447,8 +487,53 @@ sub cli_list_queues {
}
}
# }}}
+# {{{ sub UserACLList
+sub UserACLList {
+ my $userid = shift;
+ require RT::ACL;
+ my $ACLObj = new RT::ACL;
+
+ # If they're looking for rights that apply to all users.
+ if ($userid eq '-global') {
+ $ACLObj->LimitPrincipalsToUser(0);
+ }
+ else {
+ my $UserObj = new RT::User;
+ $UserObj->Load($userid);
+ $ACLObj->LimitPrincipalsToUser($UserObj->Id());
+ }
+ while (my $ACEObj = $ACLObj->Next()) {
+ print $ACEObj->Scope . "/".$ACEObj->AppliesTo . ": ".$ACEObj->Right."\n";
+ }
+}
+
+# }}}
+# {{{ sub QueueACLList
+sub QueueACLList {
+ my $queueid = shift;
+ my ($ACLObj, $QueueObj, $ACEObj);
+ # if they're looking for things that apply to 'all queues'
+ if ($queueid eq '-global') {
+ require RT::ACL;
+ $ACLObj = new RT::ACL;
+ $ACLObj->LimitScopeToQueue(0);
+ }
+ else {
+ $QueueObj = new RT::Queue;
+ $QueueObj->Load($queueid);
+ $ACLObj =$QueueObj->ACL;
+ }
+ while ($ACEObj = $ACLObj->Next()) {
+ print $ACEObj->PrincipalType. " ".$ACEObj->PrincipalId . " ".
+ $ACEObj->Scope . "/".$ACEObj->AppliesTo . ": ".
+ $ACEObj->Right."\n";
+ }
+}
+
+# }}}
+# {{{ sub ACLAdd
sub ACLAdd {
my $action = shift;
my $princtype = shift;
@@ -475,30 +560,29 @@ sub ACLAdd {
print "$action not a valid action\n";
return();
}
-
+}
+# }}}
# {{{ sub cli_help_rt_admin
sub cli_help_rt_admin {
print "
-user
+user
+ -enable <user>
+ -disable <user>
+
+ To implement:
-create <user> create a RT account for <user>
-modify <user> modify user info for <user>
- -delete <user> delete <user>'s RT account
- -getpwent <password> <admin> [<users>] Creates user(s) from the
- data in the /etc/passwd file. If no users are
- specified, ALL of /etc/passwd will be processed.
-
+listacl
+ -queue <queueid>/-global
+ -user <userid>/-global
-acl (add|del) <principal type> <principal id> <right> <scope> <object>
-
-
-queue -create <queue> create a new queue called <queue>
+queue to implement:
+ -create <queue> create a new queue called <queue>
-modify <queue> modify <queue>'s settings
-delete <queue> completely wipe out <queue>
- -area add <area> <queue> adds <area> to <queue>
- -area delete <area> <queue> remove <area> from <queue>
";
}
# }}}
@@ -511,7 +595,11 @@ sub LoadQueue {
# get a new queue object and fill it.
use RT::Queue;
$Queue = new RT::Queue($CurrentUser);
- $Queue->Load($queue_id) || die "Couldn't load the queue called $queue_id";
+ my $Status = $Queue->Load($queue_id);
+ if (!$status) {
+ $RT::Logger->debug( "Couldn\'t load the queue called $queue_id");
+ exit (-1);
+ }
return ($Queue);
}
# }}}
diff --git a/lib/rt/ui/cli/manipulate.pm b/lib/rt/ui/cli/manipulate.pm
index 19ba71f817..4a3c715b78 100755
--- a/lib/rt/ui/cli/manipulate.pm
+++ b/lib/rt/ui/cli/manipulate.pm
@@ -38,7 +38,7 @@ sub GetCurrentUser {
# {{{ sub ParseArgs
sub ParseArgs {
-
+ my $Message;
for ($i=0;$i<=$#ARGV;$i++) {
if ($ARGV[$i] eq "-create") {
&cli_create_req;
@@ -100,7 +100,9 @@ sub ParseArgs {
elsif ($ARGV[$i] eq "-take") {
my $id = int($ARGV[++$i]);
my $Ticket = &LoadTicket($id);
- $Message .= $Ticket->Take();
+ my ($trans, $msg);
+ ($trans, $msg) = $Ticket->Take;
+ $Message .= $msg;
}
elsif ($ARGV[$i] eq "-stall") {
@@ -232,10 +234,10 @@ sub ParseArgs {
my $due_string=$ARGV[++$i];
require Date::Manip;
- $due_date = &Date::Manip::ParseDate($due_string);
+ $due_date = &Date::Manip::UnixDate($due_string, "%s");
my $date = new RT::Date;
- $date->Set(Format => 'DateManip',
+ $date->Set(Format => 'unix',
Value => $due_date);
$Message .= $Ticket->SetDue($date->ISO, $CurrentUser->Id);
@@ -305,8 +307,8 @@ sub ParseArgs {
# {{{ sub cli_create_req
sub cli_create_req {
- my ($queue_id,$owner,$Requestors,$status,$priority,$Subject,$final_prio,
- $Cc, $Bcc, $date_due, $due_string, $Owner);
+ my ($queue_id,$owner,$Requestors,$status,$priority,$Subject,$final_prio, $due,
+ $Cc, $Bcc, $date_due,$due_iso, $due_string, $Owner);
require RT::Ticket;
my $Ticket = RT::Ticket->new($CurrentUser);
@@ -315,14 +317,13 @@ sub cli_create_req {
require RT::Queue;
my $Queue = RT::Queue->new($CurrentUser);
-
while ($Queue->Load($queue_id) eq undef ) {
print "That Queue does not exist\n";
$queue_id=&rt::ui::cli::question_string("Place Request in queue",);
}
-
- if (!$Queue->CurrentUserHasRight("CreateTicket")) {
- print "You may not create a ticket in that queue";
+ unless ($Queue->CurrentUserHasRight('CreateTicket')) {
+ print "No permission to create tickets in '".$Queue->QueueId."'\n";
+ return();
}
@@ -346,13 +347,17 @@ sub cli_create_req {
$due_string=&rt::ui::cli::question_string("Date due (MM/DD/YYYY)",);
if ($due_string ne '') {
require Date::Manip;
- $date_due = &Date::Manip::ParseDate($due_string);
+ $date_due = &Date::Manip::UnixDate($due_string, "%s");
}
- my $due = new RT::Date;
- $due->Set(Format => 'DateManip',
- Value => $due_date);
-
+ $due = new RT::Date;
+ $due->Set(Format => 'unix',
+ Value => $date_due);
+ $due_iso = $due->ISO;
+ }
+ else {
+ $Owner = new RT::User;
+ $Owner->Load('Nobody');
}
$Requestor = &rt::ui::cli::question_string("Requestor",);
@@ -378,20 +383,21 @@ sub cli_create_req {
Data => $content||"");
- # print "Message CC is ". $message->head->get('From');
-
- my ($id, $Transaction, $ErrStr) = $Ticket->Create ( QueueTag => $queue_id,
+ my ($id, $Transaction, $ErrStr) = $Ticket->Create ( Queue => $queue_id,
# Alias => $alias,
Owner => $Owner->id,
Subject => $Subject,
InitialPriority => $priority,
FinalPriority => $final_priority,
Status => 'open',
- Due => $due->ISO,
+ Due => $due_iso,
MIMEObj => $Message
);
-
- printf("Request %s created\n",$id);
+ if ($id == 0) {
+ print "Ticket creation aborted: $ErrStr\n";
+ return();
+ }
+ printf("Ticket %s created\n",$id);
}
# }}}
@@ -593,10 +599,10 @@ sub LoadTicket {
#print "Current User is ".$CurrentUser->Id."\n";;
require RT::Ticket;
$Ticket = RT::Ticket->new($CurrentUser);
- ($Status, $Message) = $Ticket->Load($id);
- if (!$Status) {
- print ("The ticket could not be loaded\n$Message\n");
- return (0);
+ ($Status) = $Ticket->Load($id);
+ if ($Status == 0) {
+ print ("Ticket $id could not be loaded\n");
+ exit (-1);
}
else {
return ($Ticket);