#!/usr/bin/perl -w # BEGIN LICENSE BLOCK # # Copyright (c) 1996-2003 Jesse Vincent # # (Except where explictly superceded by other copyright notices) # # 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. # # Unless otherwise specified, all modifications, corrections or # extensions to this work which alter its source code become the # property of Best Practical Solutions, LLC when submitted for # inclusion in the work. # # # END LICENSE BLOCK use strict; use vars qw($PROMPT $VERSION $Handle $Nobody $SystemUser $item); use vars qw(@Groups @Users @ACL @Queues @ScripActions @ScripConditions @Templates @CustomFields @Scrips); use lib "/opt/rt3/lib"; #This drags in RT's config.pm # We do it in a begin block because RT::Handle needs to know the type to do its # inheritance use RT; use Carp; use RT::User; use RT::CurrentUser; use RT::Template; use RT::ScripAction; use RT::ACE; use RT::Group; use RT::User; use RT::Queue; use RT::ScripCondition; use RT::CustomField; use RT::Scrip; RT::LoadConfig(); use Term::ReadKey; use Getopt::Long; my %args; GetOptions( \%args, 'prompt-for-dba-password', 'force', 'debug', 'action=s', 'dba=s', 'dba-password=s', 'datafile=s', 'datadir=s' ); $| = 1; #unbuffer that output. require RT::Handle; my $Handle = RT::Handle->new($RT::DatabaseType); $Handle->BuildDSN; my $dbh; if ( $args{'prompt-for-dba-password'} ) { $args{'dba-password'} = get_dba_password(); chomp( $args{'dba-password'} ); } unless ( $args{'action'} ) { help(); die; } if ( $args{'action'} eq 'init' ) { $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr"; print "Now creating a database for RT.\n"; if ($RT::DatabaseType ne 'Oracle' || $args{'dba'} ne $RT::DatabaseUser) { create_db(); } else { print "...skipped as ".$args{'dba'} ." is not " . $RT::DatabaseUser . " or we're working with Oracle.\n"; } $dbh->disconnect; $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die $DBI::errstr; print "Now populating database schema.\n"; insert_schema(); print "Now inserting database ACLs\n"; insert_acl() unless ($RT::DatabaseType eq 'Oracle'); print "Now inserting RT core system objects\n"; insert_initial_data(); print "Now inserting RT data\n"; insert_data( $RT::EtcPath . "/initialdata" ); } elsif ( $args{'action'} eq 'drop' ) { unless ( $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) ) { warn $DBI::errstr; warn "Database doesn't appear to exist. Aborting database drop."; exit(0); } drop_db(); } elsif ( $args{'action'} eq 'insert_initial' ) { insert_initial_data(); } elsif ( $args{'action'} eq 'insert' ) { insert_data( $args{'datafile'} ); } elsif ($args{'action'} eq 'acl') { $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr"; insert_acl($args{'datadir'}); } elsif ($args{'action'} eq 'schema') { $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr"; insert_schema($args{'datadir'}); } else { print STDERR '$0 called with an invalid --action parameter'; exit(-1); } # {{{ sub insert_schema sub insert_schema { my $base_path = (shift || $RT::EtcPath); my (@schema); print "Creating database schema.\n"; if ( -f $base_path . "/schema." . $RT::DatabaseType ) { no warnings 'unopened'; open( SCHEMA, "<" . $base_path . "/schema." . $RT::DatabaseType ); open( SCHEMA_LOCAL, "<" . $RT::LocalEtcPath . "/schema." . $RT::DatabaseType ); my $statement = ""; foreach my $line (, ($_ = ';;'), ) { $line =~ s/\#.*//g; $line =~ s/--.*//g; $statement .= $line; if ( $line =~ /;(\s*)$/ ) { $statement =~ s/;(\s*)$//g; push @schema, $statement; $statement = ""; } } local $SIG{__WARN__} = sub {}; my $is_local = 0; # local/etc/schema needs to be nonfatal. foreach my $statement (@schema) { if ($statement =~ /^\s*;$/) { $is_local = 1; next; } print STDERR "SQL: $statement\n" if defined $args{'debug'}; my $sth = $dbh->prepare($statement) or die $dbh->errstr; unless ( $sth->execute or $is_local ) { die "Problem with statement:\n $statement\n" . $sth->errstr; } } } else { die "Couldn't find schema file for " . $RT::DatabaseType . "\n"; } print "schema sucessfully inserted\n"; } # }}} # {{{ sub drop_db sub drop_db { return if ( $RT::DatabaseType eq 'SQLite' ); if ( $RT::DatabaseType eq 'Oracle' ) { print <do("Drop DATABASE $RT::DatabaseName") or warn $DBI::errstr; } # }}} # {{{ sub create_db sub create_db { print "Creating $RT::DatabaseType database $RT::DatabaseName.\n"; if ( $RT::DatabaseType eq 'SQLite' ) { return; } elsif ( $RT::DatabaseType eq 'Pg' ) { $dbh->do("CREATE DATABASE $RT::DatabaseName WITH ENCODING='UNICODE'"); if ($DBI::errstr) { $dbh->do("CREATE DATABASE $RT::DatabaseName") || die $DBI::errstr; } } elsif ($RT::DatabaseType eq 'Oracle') { insert_acl(); } elsif ( $RT::DatabaseType eq 'Informix' ) { $ENV{DB_LOCALE} = 'en_us.utf8'; $dbh->do("CREATE DATABASE $RT::DatabaseName WITH BUFFERED LOG"); } else { $dbh->do("CREATE DATABASE $RT::DatabaseName") or die $DBI::errstr; } } # }}} sub get_dba_password { print "In order to create a new database and grant RT access to that database,\n"; print "this script needs to connect to your " . $RT::DatabaseType . " instance on " . $RT::DatabaseHost . " as " . $args{'dba'} . ".\n"; print "Please specify that user's database password below. If the user has no database\n"; print "password, just press return.\n\n"; print "Password: "; ReadMode('noecho'); my $password = ReadLine(0); ReadMode('normal'); return ($password); } # {{{ sub _yesno sub _yesno { print "Proceed [y/N]:"; my $x = scalar(); $x =~ /^y/i; } # }}} # {{{ insert_acls sub insert_acl { my $base_path = (shift || $RT::EtcPath); if ( $RT::DatabaseType =~ /^oracle$/i ) { do $base_path . "/acl.Oracle" || die "Couldn't find ACLS for Oracle\n" . $@; } elsif ( $RT::DatabaseType =~ /^pg$/i ) { do $base_path . "/acl.Pg" || die "Couldn't find ACLS for Pg\n" . $@; } elsif ( $RT::DatabaseType =~ /^mysql$/i ) { do $base_path . "/acl.mysql" || die "Couldn't find ACLS for mysql in " . $RT::EtcPath . "\n" . $@; } elsif ( $RT::DatabaseType =~ /^informix$/i ) { do $base_path . "/acl.Informix" || die "Couldn't find ACLS for Informix in " . $RT::EtcPath . "\n" . $@; } elsif ( $RT::DatabaseType =~ /^SQLite$/i ) { return; } else { die "Unknown RT database type"; } my @acl = acl($dbh); foreach my $statement (@acl) { print STDERR $statement if $args{'debug'}; my $sth = $dbh->prepare($statement) or die $dbh->errstr; unless ( $sth->execute ) { die "Problem with statement:\n $statement\n" . $sth->errstr; } } } # }}} =head2 get_system_dsn Returns a dsn suitable for database creates and drops and user creates and drops =cut sub get_system_dsn { my $dsn = $Handle->DSN; #with mysql, you want to connect sans database to funge things if ( $RT::DatabaseType eq 'mysql' ) { $dsn =~ s/dbname=$RT::DatabaseName//; # with postgres, you want to connect to database1 } elsif ( $RT::DatabaseType eq 'Pg' ) { $dsn =~ s/dbname=$RT::DatabaseName/dbname=template1/; } elsif ( $RT::DatabaseType eq 'Informix' ) { # with Informix, you want to connect sans database: $dsn =~ s/Informix:$RT::DatabaseName/Informix:/; } return $dsn; } sub insert_initial_data { RT::InitLogging(); #connect to the db, for actual RT work require RT::Handle; $RT::Handle = RT::Handle->new(); $RT::Handle->Connect(); #Put together a current user object so we can create a User object my $CurrentUser = new RT::CurrentUser(); print "Checking for existing system user ($CurrentUser)..."; my $test_user = RT::User->new($CurrentUser); $test_user->Load('RT_System'); if ( $test_user->id ) { print "found!\n\nYou appear to have a functional RT database.\n" . "Exiting, so as not to clobber your existing data.\n"; exit(-1); } else { print "not found. This appears to be a new installation.\n"; } print "Creating system user..."; my $RT_System = new RT::User($CurrentUser); my ( $val, $msg ) = $RT_System->_BootstrapCreate( Name => 'RT_System', RealName => 'The RT System itself', Comments => 'Do not delete or modify this user. It is integral to RT\'s internal database structures', Creator => '1' ); unless ($val) { print "$msg\n"; exit(1); } print "done.\n"; $RT::Handle->Disconnect(); } # load some sort of data into the database sub insert_data { my $datafile = shift; #Connect to the database and get RT::SystemUser and RT::Nobody loaded RT::Init; my $CurrentUser = RT::CurrentUser->new(); $CurrentUser->LoadByName('RT_System'); if ( $datafile eq $RT::EtcPath . "/initialdata" ) { print "Creating Superuser ACL..."; my $superuser_ace = RT::ACE->new($CurrentUser); $superuser_ace->_BootstrapCreate( PrincipalId => ACLEquivGroupId( $CurrentUser->Id ), PrincipalType => 'Group', RightName => 'SuperUser', ObjectType => 'RT::System', ObjectId => '1' ); } # Slurp in stuff to insert from the datafile. Possible things to go in here:- # @groups, @users, @acl, @queues, @ScripActions, @ScripConditions, @templates require $datafile || die "Couldn't find initial data for import\n" . $@; if (@Groups) { print "Creating groups..."; foreach $item (@Groups) { my $new_entry = RT::Group->new($CurrentUser); my ( $return, $msg ) = $new_entry->_Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } if (@Users) { print "Creating users..."; foreach $item (@Users) { my $new_entry = new RT::User($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } if (@Queues) { print "Creating queues..."; for $item (@Queues) { my $new_entry = new RT::Queue($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); print "(Error: $msg)" unless ($return); print $return. "."; } print "done.\n"; } if (@ACL) { print "Creating ACL..."; for my $item (@ACL) { my ($princ, $object); # Global rights or Queue rights? if ($item->{'Queue'}) { $object = RT::Queue->new($CurrentUser); $object->Load( $item->{'Queue'} ); } else { $object = $RT::System; } # Group rights or user rights? if ($item->{'GroupDomain'}) { $princ = RT::Group->new($CurrentUser); if ($item->{'GroupDomain'} eq 'UserDefined') { $princ->LoadUserDefinedGroup( $item->{'GroupId'} ); } elsif ($item->{'GroupDomain'} eq 'SystemInternal') { $princ->LoadSystemInternalGroup( $item->{'GroupType'} ); } elsif ($item->{'GroupDomain'} eq 'RT::Queue-Role' && $item->{'Queue'}) { $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'}, Queue => $object->id); } else { $princ->Load( $item->{'GroupId'} ); } } else { $princ = RT::User->new($CurrentUser); $princ->Load( $item->{'UserId'} ); } # Grant it my ( $return, $msg ) = $princ->PrincipalObj->GrantRight( Right => $item->{'Right'}, Object => $object ); if ($return) { print $return. "."; } else { print $msg . "."; } } print "done.\n"; } if (@CustomFields) { print "Creating custom fields..."; for $item (@CustomFields) { my $new_entry = new RT::CustomField($CurrentUser); my $values = $item->{'Values'}; delete $item->{'Values'}; my $q = $item->{'Queue'}; my $q_obj = RT::Queue->new($CurrentUser); $q_obj->Load($q); if ( $q_obj->Id ) { $item->{'Queue'} = $q_obj->Id; } elsif ( $q == 0 ) { $item->{'Queue'} = 0; } else { print "(Error: Could not find queue " . $q . ")\n" unless ( $q_obj->Id ); next; } my ( $return, $msg ) = $new_entry->Create(%$item); foreach my $value ( @{$values} ) { my ( $eval, $emsg ) = $new_entry->AddValue(%$value); print "(Error: $emsg)\n" unless ($eval); } print "(Error: $msg)\n" unless ($return); print $return. "."; } print "done.\n"; } if (@ScripActions) { print "Creating ScripActions..."; for $item (@ScripActions) { my $new_entry = RT::ScripAction->new($CurrentUser); my $return = $new_entry->Create(%$item); print $return. "."; } print "done.\n"; } if (@ScripConditions) { print "Creating ScripConditions..."; for $item (@ScripConditions) { my $new_entry = RT::ScripCondition->new($CurrentUser); my $return = $new_entry->Create(%$item); print $return. "."; } print "done.\n"; } if (@Templates) { print "Creating templates..."; for $item (@Templates) { my $new_entry = new RT::Template($CurrentUser); my $return = $new_entry->Create(%$item); print $return. "."; } print "done.\n"; } if (@Scrips) { print "Creating scrips..."; for $item (@Scrips) { my $new_entry = new RT::Scrip($CurrentUser); my ( $return, $msg ) = $new_entry->Create(%$item); if ($return) { print $return. "."; } else { print "(Error: $msg)\n"; } } print "done.\n"; } $RT::Handle->Disconnect(); } =head2 ACLEquivGroupId Given a userid, return that user's acl equivalence group =cut sub ACLEquivGroupId { my $username = shift; my $user = RT::User->new($RT::SystemUser); $user->Load($username); my $equiv_group = RT::Group->new($RT::SystemUser); $equiv_group->LoadACLEquivalenceGroup($user); return ( $equiv_group->Id ); } sub help { print <