]> git.uio.no Git - usit-rt.git/blame - bin/rt-crontool
Upgrade to 4.0.8 with modification of ExternalAuth.
[usit-rt.git] / bin / rt-crontool
CommitLineData
84fb5b46
MKG
1#!/usr/bin/perl
2# BEGIN BPS TAGGED BLOCK {{{
3#
4# COPYRIGHT:
5#
6# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
7# <sales@bestpractical.com>
8#
9# (Except where explicitly superseded by other copyright notices)
10#
11#
12# LICENSE:
13#
14# This work is made available to you under the terms of Version 2 of
15# the GNU General Public License. A copy of that license should have
16# been provided with this software, but in any event can be snarfed
17# from www.gnu.org.
18#
19# This work is distributed in the hope that it will be useful, but
20# WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22# General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with this program; if not, write to the Free Software
26# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27# 02110-1301 or visit their web page on the internet at
28# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
29#
30#
31# CONTRIBUTION SUBMISSION POLICY:
32#
33# (The following paragraph is not intended to limit the rights granted
34# to you to modify and distribute this software under the terms of
35# the GNU General Public License and is only of importance to you if
36# you choose to contribute your changes and enhancements to the
37# community by submitting them to Best Practical Solutions, LLC.)
38#
39# By intentionally submitting any modifications, corrections or
40# derivatives to this work, or any other work intended for use with
41# Request Tracker, to Best Practical Solutions, LLC, you confirm that
42# you are the copyright holder for those contributions and you grant
43# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
44# royalty-free, perpetual, license to use, copy, create derivative
45# works based on those contributions, and sublicense and distribute
46# those contributions and any derivatives thereof.
47#
48# END BPS TAGGED BLOCK }}}
49use strict;
50use Carp;
51
84fb5b46
MKG
52# fix lib paths, some may be relative
53BEGIN {
54 require File::Spec;
55 my @libs = ("lib", "local/lib");
56 my $bin_path;
57
58 for my $lib (@libs) {
59 unless ( File::Spec->file_name_is_absolute($lib) ) {
60 unless ($bin_path) {
61 if ( File::Spec->file_name_is_absolute(__FILE__) ) {
62 $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
63 }
64 else {
65 require FindBin;
66 no warnings "once";
67 $bin_path = $FindBin::Bin;
68 }
69 }
70 $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
71 }
72 unshift @INC, $lib;
73 }
74
75}
76
77use RT;
78
79use Getopt::Long;
80
81use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
82
83#Clean out all the nasties from the environment
84CleanEnv();
85
86my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
87 $template, $template_id, $transaction, $transaction_type, $help, $log, $verbose );
88GetOptions(
89 "search=s" => \$search,
90 "search-arg=s" => \$search_arg,
91 "condition=s" => \$condition,
92 "condition-arg=s" => \$condition_arg,
93 "action-arg=s" => \$action_arg,
94 "action=s" => \$action,
95 "template=s" => \$template,
96 "template-id=s" => \$template_id,
97 "transaction=s" => \$transaction,
98 "transaction-type=s" => \$transaction_type,
99 "log=s" => \$log,
100 "verbose|v" => \$verbose,
101 "help" => \$help,
102);
103
104# Load the config file
105RT::LoadConfig();
106
107# adjust logging to the screen according to options
108RT->Config->Set( LogToScreen => $log ) if $log;
109
110#Connect to the database and get RT::SystemUser and RT::Nobody loaded
111RT::Init();
112
113require RT::Tickets;
114require RT::Template;
115
116#Get the current user all loaded
117my $CurrentUser = GetCurrentUser();
118
119# show help even if there is no current user
120help() if $help;
121
122unless ( $CurrentUser->Id ) {
123 print loc("No RT user found. Please consult your RT administrator.");
124 exit(1);
125}
126
127help() unless $search && $action;
128
129$transaction = lc( $transaction||'' );
130if ( $transaction && $transaction !~ /^(first|all|last)$/i ) {
131 print STDERR loc("--transaction argument could be only 'first', 'last' or 'all'");
132 exit 1;
133}
134
135if ( $template && $template_id ) {
136 print STDERR loc("--template-id is deprecated argument and can not be used with --template");
137 exit 1;
138}
139elsif ( $template_id ) {
140# don't warn
141 $template = $template_id;
142}
143
144# We _must_ have a search object
145load_module($search);
146load_module($action) if ($action);
147load_module($condition) if ($condition);
148
149my $void_scrip = RT::Scrip->new( $CurrentUser );
150my $void_scrip_action = RT::ScripAction->new( $CurrentUser );
151
152#At the appointed time:
153
154#find a bunch of tickets
155my $tickets = RT::Tickets->new($CurrentUser);
156my $search = $search->new(
157 TicketsObj => $tickets,
158 Argument => $search_arg,
159 CurrentUser => $CurrentUser
160);
161
162$search->Prepare();
163
164# TicketsFound is an RT::Tickets object
165my $tickets = $search->TicketsObj;
166
167#for each ticket we've found
168while ( my $ticket = $tickets->Next() ) {
169 print $ticket->Id() . ":\n" if ($verbose);
170
171 my $template_obj = get_template( $ticket );
172
173 if ( $transaction ) {
174 my $txns = get_transactions($ticket);
175 my $found = 0;
176 while ( my $txn = $txns->Next ) {
177 print "\t".loc("Using transaction #[_1]...", $txn->id)."\n"
178 if $verbose;
179 process($ticket, $txn, $template_obj);
180 $found = 1;
181 }
182 print "\t".loc("Couldn't find suitable transaction, skipping")."\n"
183 if $verbose && !$found;
184 } else {
185 print "\t".loc("Processing without transaction, some conditions and actions may fail. Consider using --transaction argument")."\n"
186 if $verbose;
187
188 process($ticket, undef, $template_obj);
189 }
190}
191
192sub process {
193 my $ticket = shift;
194 my $transaction = shift;
195 my $template_obj = shift;
196
197 # perform some more advanced check
198 if ($condition) {
199 my $condition_obj = $condition->new(
200 TransactionObj => $transaction,
201 TicketObj => $ticket,
202 ScripObj => $void_scrip,
203 TemplateObj => $template_obj,
204 Argument => $condition_arg,
205 CurrentUser => $CurrentUser,
206 );
207
208 # if the condition doesn't apply, get out of here
209
210 return unless $condition_obj->IsApplicable;
211 print "\t".loc("Condition matches...")."\n" if $verbose;
212 }
213
214 #prepare our action
215 my $action_obj = $action->new(
216 TicketObj => $ticket,
217 TransactionObj => $transaction,
218 TemplateObj => $template_obj,
219 Argument => $action_arg,
220 ScripObj => $void_scrip,
221 ScripActionObj => $void_scrip_action,
222 CurrentUser => $CurrentUser,
223 );
224
225 #if our preparation, move onto the next ticket
226 return unless $action_obj->Prepare;
227 print "\t".loc("Action prepared...")."\n" if $verbose;
228
229 #commit our action.
230 return unless $action_obj->Commit;
231 print "\t".loc("Action committed.")."\n" if $verbose;
232}
233
234# =head2 get_transactions
235#
236# Takes ticket and returns L<RT::Transactions> object with transactions
237# of the ticket according to command line arguments C<--transaction>
238# and <--transaction-type>.
239#
240# =cut
241
242sub get_transactions {
243 my $ticket = shift;
244 my $txns = $ticket->Transactions;
245 my $order = $transaction eq 'last'? 'DESC': 'ASC';
246 $txns->OrderByCols(
247 { FIELD => 'Created', ORDER => $order },
248 { FIELD => 'id', ORDER => $order },
249 );
250 if ( $transaction_type ) {
251 $transaction_type =~ s/^\s+//;
252 $transaction_type =~ s/\s+$//;
253 foreach my $type ( split /\s*,\s*/, $transaction_type ) {
254 $txns->Limit( FIELD => 'Type', VALUE => $type, ENTRYAGGREGATOR => 'OR' );
255 }
256 }
257 $txns->RowsPerPage(1) unless $transaction eq 'all';
258 return $txns;
259}
260
261# =head2 get_template
262#
263# Takes a ticket and returns a template according to command line options.
264#
265# =cut
266
267{ my $cache = undef;
268sub get_template {
269 my $ticket = shift;
270 return undef unless $template;
271
272 unless ( $template =~ /\D/ ) {
273 # by id
274 return $cache if $cache;
275
276 my $cache = RT::Template->new( RT->SystemUser );
277 $cache->Load( $template );
278 die "Failed to load template '$template'"
279 unless $cache->id;
280 return $cache;
281 }
282
283 my $queue = $ticket->Queue;
284 return $cache->{ $queue } if $cache->{ $queue };
285
286 my $res = RT::Template->new( RT->SystemUser );
287 $res->LoadQueueTemplate( Queue => $queue, Name => $template );
288 unless ( $res->id ) {
289 $res->LoadGlobalTemplate( $template );
290 die "Failed to load template '$template', either for queue #$queue or global"
291 unless $res->id;
292 }
293 return $cache->{ $queue } = $res;
294} }
295
296
297# =head2 load_module
298#
299# Loads a perl module, dying nicely if it can't find it.
300#
301# =cut
302
303sub load_module {
304 my $modname = shift;
305 eval "require $modname";
306 if ($@) {
307 die loc( "Failed to load module [_1]. ([_2])", $modname, $@ );
308 }
309
310}
311
312
313
314# =head2 loc LIST
315#
316# Localize this string, with the current user's currentuser object
317#
318# =cut
319
320sub loc {
321 $CurrentUser->loc(@_);
322}
323
324
325sub help {
326
327 print loc( "[_1] is a tool to act on tickets from an external scheduling tool, such as cron.", $0 )
328 . "\n";
329 print loc("It takes several arguments:") . "\n\n";
330
331 print " "
332 . loc( "[_1] - Specify the search module you want to use", "--search" )
333 . "\n";
334 print " "
335 . loc( "[_1] - An argument to pass to [_2]", "--search-arg", "--search" )
336 . "\n";
337
338 print " "
339 . loc( "[_1] - Specify the condition module you want to use", "--condition" )
340 . "\n";
341 print " "
342 . loc( "[_1] - An argument to pass to [_2]", "--condition-arg", "--condition" )
343 . "\n";
344 print " "
345 . loc( "[_1] - Specify the action module you want to use", "--action" )
346 . "\n";
347 print " "
348 . loc( "[_1] - An argument to pass to [_2]", "--action-arg", "--action" )
349 . "\n";
350 print " "
351 . loc( "[_1] - Specify name or id of template(s) you want to use", "--template" )
352 . "\n";
353 print " "
354 . loc( "[_1] - Specify if you want to use either 'first', 'last' or 'all' transactions", "--transaction" )
355 . "\n";
356 print " "
357 . loc( "[_1] - Specify the comma separated list of transactions' types you want to use", "--transaction-type" )
358 . "\n";
359 print " "
360 . loc( "[_1] - Adjust LogToScreen config option", "--log" ) . "\n";
361 print " "
362 . loc( "[_1] - Output status updates to STDOUT", "--verbose" ) . "\n";
363 print "\n";
364 print "\n";
365 print loc("Security:")."\n";
366 print loc("This tool allows the user to run arbitrary perl modules from within RT.")." ".
367 loc("If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT.")." ".
368 loc("It is incredibly important that nonprivileged users not be allowed to run this tool."). " " .
369 loc("It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool.")."\n";
370 print "\n";
371 print loc("Example:");
372 print "\n";
373 print " "
374 . loc( "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they are overdue:"
375 )
376 . "\n\n";
377
378 print " bin/rt-crontool \\\n";
379 print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
380 print " --condition RT::Condition::Overdue \\\n";
381 print " --action RT::Action::SetPriority --action-arg 99 \\\n";
382 print " --verbose\n";
383
384 print "\n";
385 print loc("Escalate tickets"). "\n";
386 print " bin/rt-crontool \\\n";
387 print " --search RT::Search::ActiveTicketsInQueue --search-arg general \\\n";
388 print" --action RT::Action::EscalatePriority\n";
389
390
391
392
393
394
395 exit(0);
396}
397
398__END__
399
400=head1 NAME
401
402rt-crontool - a tool to act on tickets from an external scheduling tool
403
404=head1 SYNOPSIS
405
406 # find all active tickets in the queue 'general' and set their priority to 99 if they are overdue:
407 rt-crontool \
408 --search RT::Search::ActiveTicketsInQueue --search-arg general \
409 --condition RT::Condition::Overdue \
410 --action RT::Action::SetPriority --action-arg 99 \
411 --verbose
412
413 # Escalate tickets
414 rt-crontool \
415 --search RT::Search::ActiveTicketsInQueue --search-arg general \
416 --action RT::Action::EscalatePriority
417
418=head1 DESCRIPTION
419
420This script is a tool to act on tickets from an external scheduling tool, such
421as cron.
422
423Security:
424
425This tool allows the user to run arbitrary perl modules from within RT. If
426this tool were setgid, a hostile local user could use this tool to gain
427administrative access to RT. It is incredibly important that nonprivileged
428users not be allowed to run this tool. It is suggested that you create a
429non-privileged unix user with the correct group membership and RT access to
430run this tool.
431
432
433=head1 OPTIONS
434
435=over
436
437=item search
438
439Specify the search module you want to use
440
441=item search-arg
442
443An argument to pass to --search
444
445=item condition
446
447Specify the condition module you want to use
448
449=item condition-arg
450
451An argument to pass to --condition
452
453=item action
454
455Specify the action module you want to use
456
457=item action-arg
458
459An argument to pass to --action
460
461=item template
462
463Specify name or id of template(s) you want to use
464
465=item transaction
466
467Specify if you want to use either 'first', 'last' or 'all' transactions
468
469
470=item transaction-type
471
472Specify the comma separated list of transactions' types you want to use
473
474=item log
475
476Adjust LogToScreen config option
477
478=item verbose
479
480Output status updates to STDOUT
481
482=back
483