Skip to content

Commit

Permalink
Add script to email users expiring auth tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
bkembreeBPS authored and sunnavy committed Sep 12, 2023
1 parent e213d6a commit 3e89b36
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
/sbin/rt-dump-metadata
/sbin/rt-email-dashboards
/sbin/rt-email-digest
/sbin/rt-email-expiring-auth-tokens
/sbin/rt-email-group-admin
/sbin/rt-externalize-attachments
/sbin/rt-fulltext-indexer
Expand Down
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ SYSTEM_BINARIES = rt-attributes-viewer \
rt-dump-metadata \
rt-email-dashboards \
rt-email-digest \
rt-email-expiring-auth-tokens \
rt-email-group-admin \
rt-externalize-attachments \
rt-fulltext-indexer \
Expand Down
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ AC_CONFIG_FILES([
sbin/rt-test-dependencies
sbin/rt-email-digest
sbin/rt-email-dashboards
sbin/rt-email-expiring-auth-tokens
sbin/rt-externalize-attachments
sbin/rt-clean-attributes
sbin/rt-clean-sessions
Expand Down
27 changes: 27 additions & 0 deletions etc/initialdata
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,33 @@ Hour: { $SubscriptionObj->SubValue('Hour') }
}
}
},
{
Queue => '0',
Name => 'Auth tokens expiring in 7 days', # loc
Description => 'Auth tokens expiring in 7 days', # loc
Content => q[Subject: [{RT->Config->Get('rtname')}] Your have auth tokens that will expire in 7 days

Hello { $UserObj->RealName || $UserObj->Name }:

Your following auth tokens are going to expire in 7 days:

{
for my $token (@AuthTokens) {
$OUT .= " * " . $token->Description . " (expires at " . $token->ExpiresObj->AsString . ")\n";
}

if ( $UserObj->HasRight( Right => 'ModifySelf', Object => RT->System )
&& $UserObj->HasRight( Right => 'ManageAuthTokens', Object => RT->System ) )
{
$OUT .= "\nYou can revoke them and generate new ones on " . RT->Config->Get('WebURL') . 'Prefs/AuthTokens.html'
}
else {
$OUT .= "\nIf you are still using them, please contact your RT manager to generate new ones for you.";
}
}

],
},
);

@Scrips = (
Expand Down
32 changes: 32 additions & 0 deletions etc/upgrade/5.0.5/content
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use strict;
use warnings;

our @Templates = (
{
Queue => '0',
Name => 'Auth tokens expiring in 7 days', # loc
Description => 'Auth tokens expiring in 7 days', # loc
Content => q[Subject: [{RT->Config->Get('rtname')}] You have auth tokens that will expire in 7 days

Hello { $UserObj->RealName || $UserObj->Name }:

Your following auth tokens are going to expire in 7 days:

{
for my $token (@AuthTokens) {
$OUT .= " * " . $token->Description . " (expires at " . $token->ExpiresObj->AsString . ")\n";
}

if ( $UserObj->HasRight( Right => 'ModifySelf', Object => RT->System )
&& $UserObj->HasRight( Right => 'ManageAuthTokens', Object => RT->System ) )
{
$OUT .= "\nYou can revoke them and generate new ones on " . RT->Config->Get('WebURL') . 'Prefs/AuthTokens.html'
}
else {
$OUT .= "\nIf you are still using them, please contact your RT manager to generate new ones for you.";
}
}

],
},
);
283 changes: 283 additions & 0 deletions sbin/rt-email-expiring-auth-tokens.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
#!@PERL@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
#
# This software is Copyright (c) 1996-2023 Best Practical Solutions, LLC
# <[email protected]>
#
# (Except where explicitly superseded by other copyright notices)
#
#
# LICENSE:
#
# This work is made available to you under the terms of Version 2 of
# the GNU General Public License. A copy of that license should have
# been provided with this software, but in any event can be snarfed
# from www.gnu.org.
#
# This work is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
#
#
# CONTRIBUTION SUBMISSION POLICY:
#
# (The following paragraph is not intended to limit the rights granted
# to you to modify and distribute this software under the terms of
# the GNU General Public License and is only of importance to you if
# you choose to contribute your changes and enhancements to the
# community by submitting them to Best Practical Solutions, LLC.)
#
# By intentionally submitting any modifications, corrections or
# derivatives to this work, or any other work intended for use with
# Request Tracker, to Best Practical Solutions, LLC, you confirm that
# you are the copyright holder for those contributions and you grant
# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
# royalty-free, perpetual, license to use, copy, create derivative
# works based on those contributions, and sublicense and distribute
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
use warnings;
use strict;

BEGIN { # BEGIN RT CMD BOILERPLATE
require File::Spec;
require Cwd;
my @libs = ( "@RT_LIB_PATH@", "@LOCAL_LIB_PATH@" );
my $bin_path;

for my $lib (@libs) {
unless ( File::Spec->file_name_is_absolute($lib) ) {
$bin_path ||= ( File::Spec->splitpath( Cwd::abs_path(__FILE__) ) )[1];
$lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
}
unshift @INC, $lib;
}

}

use RT;
use RT::Interface::CLI qw(Init);
use RT::Interface::Email;


my ( $expires_by, $expires_on, $print, $help, $template );
my %opt = (
'expires-by=s' => \$expires_by,
'expires-on=s' => \$expires_on,
'template=s' => \$template,
'print' => \$print,
'help' => \$help,
);
Init( %opt );

if ( $help ) {
Pod::Usage::pod2usage({ verbose => 2});
exit;
}

if ( $expires_by || $expires_on ) {
if ( $expires_by && $expires_on ) {
Pod::Usage::pod2usage( { message => "Cannot use both expires-by and expires-on parameters" } );
}
}
else {
Pod::Usage::pod2usage( { message => "One of expires-by or expires-on parameter is required" } );
}

if ( !$template ) {
Pod::Usage::pod2usage( { message => "template parameter is required" } );
}

my $template_obj = RT::Template->new( RT->SystemUser );
my ( $ret, $msg ) = $template_obj->Load($template);
unless ($ret) {
print "Could not load template $template";
exit 1;
}

my $auth_tokens = RT::AuthTokens->new( RT->SystemUser );

if ($expires_by) {
my $expires_by_date = RT::Date->new( RT->SystemUser );
usage("Invalid date parameter '$expires_by'")
unless $expires_by_date->Set( Format => 'unknown', Value => $expires_by, Timezone => 'server' ) > 0;

$expires_by_date->SetToMidnight( Timezone => 'server' );

$auth_tokens->Limit(
FIELD => 'Expires',
VALUE => $expires_by_date->ISO( Timezone => 'UTC' ),
OPERATOR => '<',
ENTRYAGGREGATOR => 'AND',
);

my $today = RT::Date->new( RT->SystemUser );
$today->SetToNow;
$today->SetToMidnight( Timezone => 'server' );
$auth_tokens->Limit(
FIELD => 'Expires',
VALUE => $today->ISO( Timezone => 'UTC' ),
OPERATOR => '>=',
ENTRYAGGREGATOR => 'AND',
);
}
elsif ($expires_on) {
my $expires_on_start_date = RT::Date->new( RT->SystemUser );
my $expires_on_end_date = RT::Date->new( RT->SystemUser );
usage("Invalid date parameter '$expires_on'")
unless $expires_on_start_date->Set( Format => 'unknown', Value => $expires_on, Timezone => 'server' ) > 0;

$expires_on_start_date->SetToMidnight( Timezone => 'server' );
$expires_on_end_date->Set( Format => 'unix', Value => $expires_on_start_date->Unix );
$expires_on_end_date->AddDay;

$auth_tokens->Limit(
FIELD => 'Expires',
VALUE => $expires_on_start_date->ISO( Timezone => 'UTC' ),
OPERATOR => '>=',
ENTRYAGGREGATOR => 'AND',
);
$auth_tokens->Limit(
FIELD => 'Expires',
VALUE => $expires_on_end_date->ISO( Timezone => 'UTC' ),
OPERATOR => '<',
ENTRYAGGREGATOR => 'AND',
);
}

$auth_tokens->Limit(
FIELD => 'Expires',
VALUE => 'NULL',
OPERATOR => 'IS NOT',
ENTRYAGGREGATOR => 'AND',
);

my $users_alias = $auth_tokens->Join(
ALIAS1 => 'main',
FIELD1 => 'Owner',
TABLE2 => 'Users',
FIELD2 => 'id',
);

$auth_tokens->Limit(
ALIAS => $users_alias,
FIELD => 'EmailAddress',
VALUE => 'NULL',
OPERATOR => 'IS NOT',
ENTRYAGGREGATOR => 'AND',
);

if ( RT->Config->Get('DatabaseType') ne 'Oracle' ) {
$auth_tokens->Limit(
ALIAS => $users_alias,
FIELD => 'EmailAddress',
VALUE => '',
OPERATOR => '!=',
ENTRYAGGREGATOR => 'AND',
CASESENSITIVE => 0,
);
}

my $principals_alias = $auth_tokens->Join(
ALIAS1 => $users_alias,
FIELD1 => 'id',
TABLE2 => 'Principals',
FIELD2 => 'id',
);

$auth_tokens->Limit(
ALIAS => $principals_alias,
FIELD => 'Disabled',
VALUE => 0,
);

my %expired_tokens_by_user = ();
while ( my $auth_token = $auth_tokens->Next ) {
push @{ $expired_tokens_by_user{ $auth_token->Owner } }, $auth_token;
}

foreach my $user_id ( keys %expired_tokens_by_user ) {
my $user_obj = RT::User->new( RT->SystemUser );
$user_obj->Load($user_id);
my $user_email = $user_obj->EmailAddress;
my ( $ret, $msg ) = $template_obj->Parse( AuthTokens => $expired_tokens_by_user{$user_id}, UserObj => $user_obj );
unless ($ret) {
print "Could not to parse template: $msg\n";
exit 1;
}

# Set our sender and recipient.
if ( !$template_obj->MIMEObj->head->get('From') ) {
if ( my $from = RT::Config->Get('RTSupportEmail') || RT::Config->Get('CorrespondAddress') ) {
$template_obj->MIMEObj->head->replace( 'From', Encode::encode( "UTF-8", $from ) );
}
}
if ( !$template_obj->MIMEObj->head->get('To') ) {
$template_obj->MIMEObj->head->replace( 'To', Encode::encode( "UTF-8", $user_email ) );
}

if ($print) {
print $template_obj->MIMEObj->as_string, "\n";
}
else {
my $ok = RT::Interface::Email::SendEmail( Entity => $template_obj->MIMEObj );
if ( !$ok ) {
RT->Logger->error("Failed to send expiring auth tokens email to $user_email");
}
}
}


__END__

=head1 NAME

rt-email-expiring-auth-tokens - email users about expiring auth tokens

=head1 SYNOPSIS

rt-email-expiring-auth-tokens --expires-by '7 days' --template 'Auth tokens expiring in 7 days'

=head1 DESCRIPTION

This script is a tool to email users about their expiring auth tokens.

=head1 OPTIONS

=over

=item expires-by

All auth tokens that will expire between today and this date will be included in the email.

Format is YYYY-MM-DD or any date format supported by Time::ParseDate.

=item expires-on

All auth tokens that expire on this date will be included in the email.

Format is YYYY-MM-DD or any date format supported by Time::ParseDate.

=item template

Specify name or id of template you want to use.

=item print

Print the expiring auth tokens to STDOUT; don't email them.

=item help

Print this message

=back

0 comments on commit 3e89b36

Please sign in to comment.