package RT::CIFMinimal;
our $VERSION = '0.01';
use 5.008008;
use warnings;
use strict;
use Net::Abuse::Utils qw(:all);
use Regexp::Common qw/net URI/;
use Net::CIDR;
sub cif_data {
my $args = shift;
my $fields = $args->{'fields'} || 'restriction,guid,severity,confidence,address,rdata,portlist,protocol,impact,description,detecttime,alternativeid_restriction,alternativeid';
my $user = $args->{'user'};
my $q = $args->{'q'};
my $nolog = $args->{'nolog'} || 0;
my $results = $args->{'results'};
return unless($q);
require CIF::Client;
my $tls_verify = RT->Config->Get('CIFMinimal_TLS_Verify') || 0;
my ($client,$err) = CIF::Client->new({
host => RT->Config->Get('CIFMinimal_APIHostname') || RT->Config->Get('WebBaseURL').'/api',
simple_hashes => 1,
fields => $fields,
group_map => 1,
verify_tls => $tls_verify,
});
warn $err if($err);
last if($err);
require CIF::WebAPI::APIKey;
my @recs = CIF::WebAPI::APIKey->search(uuid_alias => $user->EmailAddress());
unless($recs[0] && $recs[0]->uuid()){
# generate apikey
require RT::CIFMinimal;
my $id = RT::CIFMinimal::generate_apikey({ user => $user, description => 'generated automatically for WebUI search' });
unless($id){
push(@$results, 'unable to automatically generate an apikey, please contact your administrator');
$RT::Logger->error('unable to generate an apikey for: '.$user->EmailAddress());
return;
} else {
push(@recs,$id);
push(@$results,'default WebUI apikey '.$id->uuid().' automatically generated');
}
}
$client->{'apikey'} = $recs[0]->uuid();
my @res;
my @qarray = split(/,/,$q);
foreach(@qarray){
my $feed = $client->GET(
query => $_,
limit => 25,
nolog => $nolog,
);
if($feed){
@recs = @{$feed->{'feed'}->{'entry'}};
if($#recs > -1){
@recs = sort { $b->{'confidence'} cmp $a->{'confidence'} } @recs;
$feed->{'feed'}->{'entry'} = \@recs;
}
require CIF::Client::Plugin::Html;
$client->{'class'} = 'collection';
$client->{'evenrowclass'} = 'evenline';
$client->{'oddrowclass'} = 'oddline';
my $t = CIF::Client::Plugin::Html->write_out($client,$feed,undef);
push(@res,$t);
} else {
if($client->responseCode != 200){
push(@$results,$client->responseContent());
}
}
}
my $text = (@res && $#res > -1) ? join("\n",@res) : '
No Results
';
return($text);
}
sub generate_apikey {
my $args = shift;
my $user = $args->{'user'};
my $key_desc = $args->{'description'};
my $default_guid = $args->{'default_guid'};
my $add_groups = $args->{'groups'};
my @a_groups = (ref($add_groups) eq 'ARRAY') ? @$add_groups : $add_groups;
return unless($user);
require CIF::WebAPI::APIKey;
if(ref($user) eq 'RT::User'){
my $g = $user->OwnGroups();
my %group_map;
while(my $grp = $g->Next()){
next unless($grp->Name() =~ /^DutyTeam (\S+)/);
my $guid = lc($1);
my $priority = $grp->FirstCustomFieldValue('CIFGroupPriority');
$group_map{$guid} = $priority;
}
$group_map{'everyone'} = 1000;
my @sorted = sort { $group_map{$a} <=> $group_map{$b} } keys(%group_map);
if($default_guid){
$default_guid = $sorted[0] unless(exists($group_map{$default_guid}));
} else {
$default_guid = $sorted[0];
}
## TODO -- fix this
unless($a_groups[0]){
@a_groups = @sorted;
} else {
foreach (@a_groups){
return unless(exists($group_map{$_}));
}
}
my $id = CIF::WebAPI::APIKey->genkey(
uuid_alias => $user->EmailAddress() || $user->Name(),
description => $key_desc,
default_guid => $default_guid,
groups => join(',',@a_groups),
);
return($id);
}
}
sub network_info {
my $addr = shift;
return if(IsPrivateAddress($addr));
my ($as,$network,$ccode,$rir,$date) = get_asn_info($addr);
my $as_desc = '';
if($as){
$as_desc = get_as_description($as);
}
return({
asn => $as,
cidr => $network,
cc => $ccode,
rir => $rir,
modified => $date,
description => $as_desc,
}) if($as);
return(0);
}
my @list = (
"0.0.0.0/8",
"10.0.0.0/8",
"127.0.0.0/8",
"192.168.0.0/16",
"169.254.0.0/16",
"192.0.2.0/24",
"224.0.0.0/4",
"240.0.0.0/5",
"248.0.0.0/5"
);
sub IsPrivateAddress {
my $addr = shift;
my $found = Net::CIDR::cidrlookup($addr,@list);
return($found);
}
sub ReportsByType {
my $user = shift;
my @called = caller();
my $type = $called[1];
my @t = split(/\//,$type);
$type = $t[$#t];
my $category = $t[$#t-1];
my $reports = RT::Tickets->new($user);
my $query = "Queue = 'Incident Reports' AND (Status = 'new' OR Status = 'open')";
$reports->FromSQL($query);
$reports->OrderByCols({FILED => 'id', ORDER => 'DESC'});
my @array;
while(my $r = $reports->Next()){
push(@array,$r->IODEF->to_tree());
}
return ('') unless($#array > -1);
require JSON;
return(JSON::to_json(\@array));
}
{
my %cache;
sub GetCustomField {
my $field = shift or return;
return $cache{ $field } if exists $cache{ $field };
my $cf = RT::CustomField->new( $RT::SystemUser );
$cf->Load( $field );
return $cache{ $field } = $cf;
}
}
use Hook::LexWrap;
use Regexp::Common;
use Regexp::Common::net::CIDR;
# on OCFV create format storage
require RT::ObjectCustomFieldValue;
wrap 'RT::ObjectCustomFieldValue::Create',
pre => sub {
my %args = @_[1..@_-2];
my $cf = GetCustomField( 'Address' );
unless ( $cf && $cf->id ) {
$RT::Logger->crit("Couldn't load IP CF");
return;
}
return unless $cf->id == $args{'CustomField'};
for ( my $i = 1; $i < @_; $i += 2 ) {
next unless $_[$i] && $_[$i] eq 'Content';
my $arg = $_[++$i];
next if ($arg =~ /^\s*$RE{net}{CIDR}{IPv4}{-keep}\s*$/go );
my ($sIP, $eIP) = RT::IR::ParseIPRange( $arg );
unless ( $sIP && $eIP ) {
#$_[-1] = 0;
return;
}
$_[$i] = $sIP;
my $flag = 0;
for ( my $j = 1; $j < @_; $j += 2 ) {
next unless $_[$j] && $_[$j] eq 'LargeContent';
$flag = $_[++$j] = $eIP;
last;
}
splice @_, -1, 0, LargeContent => $eIP unless $flag;
return;
}
};
eval "require RT::CIFMinimal_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CIFMinimal_Vendor.pm});
eval "require RT::CIFMinimal_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CIFMinimal_Local.pm});
package RT::User;
use Hook::LexWrap;
{
my $obj;
wrap 'RT::User::Create',
pre => sub {
my $user = $obj = $_[0];
my %args = (@_[1..(@_-2)]);
return if($args{'EmailAddress'});
unless($args{'EmailAddress'}){ $args{'EmailAddress'} = $args{'Name'}; }
my @res = $user->Create(%args);
$_[-1] = \@res;
},
post => sub {
return unless $_[-1];
my $val = ref $_[-1]? \$_[-1][0]: \$_[-1];
return unless($val =~ /\d+/);
if(my %map = RT->Config->Get('CIFMinimal_UserGroupMapping')){
my $x = $ENV{$map{'EnvVar'}};
my @tags = split($map{'Pattern'},$x);
my $group_map = $map{'Mapping'};
foreach(keys %$group_map){
foreach my $g (@tags){
if($g eq $_){
require RT::Group;
my $y = RT::Group->new($RT::SystemUser);
my ($ret,$err) = $y->LoadUserDefinedGroup($group_map->{$_});
$RT::Logger->debug("adding user to group: $g");
($ret,$err) = $y->AddMember($$val);
unless($ret){
$RT::Logger->error("Couldn't add user to group: ".$y->Name());
$RT::Logger->error($err);
$RT::Handle->Rollback();
return(0);
}
}
}
}
} elsif (my $default = RT->Config->Get('CIFMinimal_DefaultUserGroup')){
require RT::Group;
my $default = RT->Config->Get('CIFMinimal_DefaultUserGroup');
return unless($default);
my $group = RT::Group->new($obj->CurrentUser());
my ($ret,$err) = $group->LoadUserDefinedGroup($default);
unless($ret){
$RT::Logger->error("Couldn't add user to group: ".$default.': '.$err);
return(0);
}
($ret,$err) = $group->_AddMember(InsideTransaction => 1, PrincipalId => $$val);
unless($ret){
$RT::Logger->error("Couldn't add user to group: ".$group->Name());
$RT::logger->error($err);
$RT::Handle->Rollback();
return(0);
}
}
}
}
1;
__END__
=head1 NAME
RT::CIFMinimal - Perl extension for RT+IR integration with CIF
=head1 DESCRIPTION
This module wraps a work-flow friendly UI around CIF using the basic components found in RT.
=head1 SEE ALSO
http://code.google.com/p/collective-intelligence-framework
XML::IODEF
XML::IODEF::Simple
=head1 AUTHOR
Wes Young, Ewes@barely3am.comE
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2011 REN-ISAC and The Trustees of Indiana University
Copyright (C) 2011 by Wes Young
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.
=cut