73085: Enable credit card/ach encryption on a live system
[freeside.git] / FS / FS / Setup.pm
index 7475d37..f005a36 100644 (file)
@@ -1,19 +1,32 @@
 package FS::Setup;
+use base qw( Exporter );
 
 use strict;
-use vars qw( @ISA @EXPORT_OK );
-use Exporter;
+use vars qw( @EXPORT_OK );
 #use Tie::DxHash;
 use Tie::IxHash;
-use FS::UID qw( dbh );
-use FS::Record;
+use Crypt::OpenSSL::RSA;
+use FS::UID qw( dbh driver_name );
 
 use FS::svc_domain;
 $FS::svc_domain::whois_hack = 1;
 $FS::svc_domain::whois_hack = 1;
 
-@ISA = qw( Exporter );
-@EXPORT_OK = qw( create_initial_data );
+#populate_locales
+use Locale::Country;
+use Locale::SubCountry 1.42;
+use FS::cust_main_county;
+
+#populate_access
+use FS::AccessRight;
+use FS::access_right;
+use FS::access_groupagent;
+
+#populate_msgcat
+use FS::Record qw(qsearch);
+use FS::msgcat;
+
+@EXPORT_OK = qw( create_initial_data enable_encryption enable_banned_pay_pad );
 
 =head1 NAME
 
@@ -45,12 +58,20 @@ sub create_initial_data {
 
   populate_locales();
 
+  populate_duplock();
+
   #initial_data data
   populate_initial_data(%opt);
 
   populate_access();
 
   populate_msgcat();
+
+  populate_numbering();
+
+  enable_encryption();
+
+  enable_banned_pay_pad();
   
   if ( $oldAutoCommit ) {
     dbh->commit or die dbh->errstr;
@@ -58,50 +79,131 @@ sub create_initial_data {
 
 }
 
-sub populate_locales {
+sub enable_encryption {
+
+  eval "use FS::Conf";
+  die $@ if $@;
+
+  my $conf = new FS::Conf;
+
+  die "encryption key(s) already in place"
+    if $conf->exists('encryptionpublickey')
+    || $conf->exists('encryptionprivatekey');
+
+  my $length = 2048;
+  my $rsa = Crypt::OpenSSL::RSA->generate_key($length);
+
+  $conf->set('encryption', 1);
+  $conf->set('encryptionmodule',     'Crypt::OpenSSL::RSA');
+  $conf->set('encryptionpublickey',  $rsa->get_public_key_string );
+  $conf->set('encryptionprivatekey', $rsa->get_private_key_string );
+
+  # reload Record globals, false laziness with FS::Record
+  $FS::Record::conf_encryption           = $conf->exists('encryption');
+  $FS::Record::conf_encryptionmodule     = $conf->config('encryptionmodule');
+  $FS::Record::conf_encryptionpublickey  = join("\n",$conf->config('encryptionpublickey'));
+  $FS::Record::conf_encryptionprivatekey = join("\n",$conf->config('encryptionprivatekey'));
+
+}
+
+sub enable_banned_pay_pad {
 
-  use Locale::Country;
-  use Locale::SubCountry;
-  use FS::cust_main_county;
+  eval "use FS::Conf";
+  die $@ if $@;
+
+  my $conf = new FS::Conf;
+
+  die "banned_pay-pad already in place"
+    if length( $conf->config('banned_pay-pad') );
+
+  #arbitrary but good enough... all we need is *some* per-site random padding
+  my @pw_set = ( 'a'..'z', 'A'..'Z', '0'..'9', '(', ')', '#', '.', ',' );
+
+  $conf->set('banned_pay-pad',
+    join('', map($pw_set[ int(rand($#pw_set)) ], (0..15) ) )
+  );
+
+}
+
+sub populate_numbering {
+  eval "use FS::lata_Data;"; # this automatically populates the lata table, if unpopulated
+  eval "use FS::msa_Data;"; # this automatically populates the msa table, if unpopulated
+}
+
+sub populate_locales {
 
   #cust_main_county
   foreach my $country ( sort map uc($_), all_country_codes ) {
+    _add_country($country);
+  }
+
+}
+
+sub populate_addl_locales {
+
+  my %addl = (
+    'US' => {
+      'FM' => 'Federated States of Micronesia',
+      'MH' => 'Marshall Islands',
+      'PW' => 'Palau',
+      'AA' => "Armed Forces Americas (except Canada)",
+      'AE' => "Armed Forces Europe / Canada / Middle East / Africa",
+      'AP' => "Armed Forces Pacific",
+    },
+  );
+
+  foreach my $country ( keys %addl ) {
+    foreach my $state ( keys %{ $addl{$country} } ) {
+      # $longname = $addl{$country}{$state};
+      _add_locale( 'country'=>$country, 'state'=>$state);
+    }
+  }
+
+}
+
+sub _add_country {
+
+  my( $country ) = shift;
+
+  my $subcountry = eval { new Locale::SubCountry($country) };
+  my @states = $subcountry ? $subcountry->all_codes : undef;
   
-    my $subcountry = eval { new Locale::SubCountry($country) };
-    my @states = $subcountry ? $subcountry->all_codes : undef;
-  
-    if ( !scalar(@states) || ( scalar(@states)==1 && !defined($states[0]) ) ) {
-  
-      my $cust_main_county = new FS::cust_main_county({
-        'tax'   => 0,
-        'country' => $country,
-      });  
-      my $error = $cust_main_county->insert;
-      die $error if $error;
-  
-    } else {
-  
-      if ( $states[0] =~ /^(\d+|\w)$/ ) {
-        @states = map $subcountry->full_name($_), @states
-      }
+  if ( !scalar(@states) || ( scalar(@states)==1 && !defined($states[0]) ) ) {
+
+    _add_locale( 'country'=>$country );
   
-      foreach my $state ( @states ) {
+  } else {
   
-        my $cust_main_county = new FS::cust_main_county({
-          'state' => $state,
-          'tax'   => 0,
-          'country' => $country,
-        });  
-        my $error = $cust_main_county->insert;
-        die $error if $error;
+    if ( $states[0] =~ /^(\d+|\w)$/ ) {
+      @states = map $subcountry->full_name($_), @states
+    }
   
-      }
-    
+    foreach my $state ( @states ) {
+      _add_locale( 'country'=>$country, 'state'=>$state);
     }
+    
   }
 
 }
 
+sub _add_locale {
+  my $cust_main_county = new FS::cust_main_county( { 'tax'=>0, @_ });  
+  my $error = $cust_main_county->insert;
+  die $error if $error;
+}
+
+sub populate_duplock {
+
+  return unless driver_name =~ /^mysql/i;
+
+  my $sth = dbh->prepare(
+    "INSERT INTO duplicate_lock ( lockname ) VALUES ( 'svc_acct' )"
+  ) or die dbh->errstr;
+
+  $sth->execute or die $sth->errstr;
+
+}
+
 sub populate_initial_data {
   my %opt = @_;
 
@@ -109,18 +211,29 @@ sub populate_initial_data {
 
   foreach my $table ( keys %$data ) {
 
+    #warn "popuilating $table\n";
+
     my $class = "FS::$table";
     eval "use $class;";
     die $@ if $@;
 
+    $class->_populate_initial_data(%opt)
+      if $class->can('_populate_initial_data');
+
     my @records = @{ $data->{$table} };
 
     foreach my $record ( @records ) {
+
       my $args = delete($record->{'_insert_args'}) || [];
       my $object = $class->new( $record );
       my $error = $object->insert( @$args );
       die "error inserting record into $table: $error\n"
         if $error;
+
+      #my $pkey = $object->primary_key;
+      #my $pkeyvalue = $object->$pkey();
+      #warn "  inserted $pkeyvalue\n";
+
     }
 
   }
@@ -130,45 +243,81 @@ sub populate_initial_data {
 sub initial_data {
   my %opt = @_;
 
+  my $cust_location = FS::cust_location->new({
+      'address1'  => '1234 System Lane',
+      'city'      => 'Systemtown',
+      'state'     => 'CA',
+      'zip'       => '54321',
+      'country'   => 'US',
+  });
+
   #tie my %hash, 'Tie::DxHash', 
   tie my %hash, 'Tie::IxHash', 
 
+    #bootstrap user
+    'access_user' => [
+      { 'username'  => 'fs_bootstrap',
+        '_password' => 'changeme', #will trigger warning if you try to enable
+        'last'      => 'User',
+        'first'     => 'Bootstrap',
+        'disabled'  => 'Y',
+      },
+    ],
+
     #superuser group
     'access_group' => [
       { 'groupname' => 'Superuser' },
     ],
 
-    #billing events
-    'part_bill_event' => [
-      { 'payby'     => 'CARD',
-        'event'     => 'Batch card',
-        'seconds'   => 0,
-        'eventcode' => '$cust_bill->batch_card();',
-        'weight'    => 40,
-        'plan'      => 'batch-card',
-      },
-      { 'payby'     => 'BILL',
-        'event'     => 'Send invoice',
-        'seconds'   => 0,
-        'eventcode' => '$cust_bill->send();',
-        'weight'    => 50,
-        'plan'      => 'send',
-      },
-      { 'payby'     => 'DCRD',
-        'event'     => 'Send invoice',
-        'seconds'   => 0,
-        'eventcode' => '$cust_bill->send();',
-        'weight'    => 50,
-        'plan'      => 'send',
-      },
-      { 'payby'     => 'DCHK',
-        'event'     => 'Send invoice',
-        'seconds'   => 0,
-        'eventcode' => '$cust_bill->send();',
-        'weight'    => 50,
-        'plan'      => 'send',
-      },
-    ],
+    #reason types
+    'reason_type' => [],
+
+#XXX need default new-style billing events
+#    #billing events
+#    'part_bill_event' => [
+#      { 'payby'     => 'CARD',
+#        'event'     => 'Batch card',
+#        'seconds'   => 0,
+#        'eventcode' => '$cust_bill->batch_card(%options);',
+#        'weight'    => 40,
+#        'plan'      => 'batch-card',
+#      },
+#      { 'payby'     => 'BILL',
+#        'event'     => 'Send invoice',
+#        'seconds'   => 0,
+#        'eventcode' => '$cust_bill->send();',
+#        'weight'    => 50,
+#        'plan'      => 'send',
+#      },
+#      { 'payby'     => 'DCRD',
+#        'event'     => 'Send invoice',
+#        'seconds'   => 0,
+#        'eventcode' => '$cust_bill->send();',
+#        'weight'    => 50,
+#        'plan'      => 'send',
+#      },
+#      { 'payby'     => 'DCHK',
+#        'event'     => 'Send invoice',
+#        'seconds'   => 0,
+#        'eventcode' => '$cust_bill->send();',
+#        'weight'    => 50,
+#        'plan'      => 'send',
+#      },
+#      { 'payby'     => 'DCLN',
+#        'event'     => 'Suspend',
+#        'seconds'   => 0,
+#        'eventcode' => '$cust_bill->suspend();',
+#        'weight'    => 40,
+#        'plan'      => 'suspend',
+#      },
+#      #{ 'payby'     => 'DCLN',
+#      #  'event'     => 'Retriable',
+#      #  'seconds'   => 0,
+#      #  'eventcode' => '$cust_bill_event->retriable();',
+#      #  'weight'    => 60,
+#      #  'plan'      => 'retriable',
+#      #},
+#    ],
     
     #you must create a service definition. An example of a service definition
     #would be a dial-up account or a domain. First, it is necessary to create a
@@ -208,7 +357,7 @@ sub initial_data {
     #which would only sell regular packages of services. Click on View/Edit
     #agent types and Add a new agent type.
     'agent_type' => [
-      { 'atype' => 'internal' },
+      { 'atype' => 'Internal' },
     ],
 
     #Allow this agent type to sell the package you created above.
@@ -240,18 +389,13 @@ sub initial_data {
     #with billing type Complimentary. Leave the First package dropdown set to
     #(none).
     'cust_main' => [
-      { 'agentnum'  => 1, #XXX
-        'refnum'    => 1, #XXX
-        'first'     => 'System',
-        'last'      => 'Accounts',
-        'address1'  => '1234 System Lane',
-        'city'      => 'Systemtown',
-        'state'     => 'CA',
-        'zip'       => '54321',
-        'country'   => 'US',
-        'payby'     => 'COMP',
-        'payinfo'   => 'system', #or something
-        'paydate'   => '1/2037',
+      { 'agentnum'      => 1, #XXX
+        'refnum'        => 1, #XXX
+        'first'         => 'System',
+        'last'          => 'Accounts',
+        'complimentary' => 'Y',
+        'bill_location' => $cust_location,
+        'ship_location' => $cust_location,
       },
     ],
 
@@ -282,7 +426,15 @@ sub initial_data {
 
     #not yet....
 
-  #)
+    #usage classes
+    'usage_class' => [],
+
+    #phone types
+    'phone_type' => [],
+
+    #message templates
+    'msg_template' => [],
+
   ;
 
   \%hash;
@@ -291,10 +443,7 @@ sub initial_data {
 
 sub populate_access {
 
-  use FS::AccessRight;
-  use FS::access_right;
-
-  foreach my $rightname ( FS::AccessRight->rights ) {
+  foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
     my $access_right = new FS::access_right {
       'righttype'   => 'FS::access_group',
       'rightobjnum' => 1, #$supergroup->groupnum,
@@ -317,15 +466,12 @@ sub populate_access {
 
 sub populate_msgcat {
 
-  use FS::Record qw(qsearch);
-  use FS::msgcat;
-
   foreach my $del_msgcat ( qsearch('msgcat', {}) ) {
     my $error = $del_msgcat->delete;
     die $error if $error;
   }
 
-  my %messages = msgcat_messages();
+  my %messages = FS::msgcat::_legacy_messages();
 
   foreach my $msgcode ( keys %messages ) {
     foreach my $locale ( keys %{$messages{$msgcode}} ) {
@@ -341,106 +487,6 @@ sub populate_msgcat {
 
 }
 
-sub msgcat_messages {
-
-  #  'msgcode' => {
-  #    'en_US' => 'Message',
-  #  },
-
-  (
-
-    'passwords_dont_match' => {
-      'en_US' => "Passwords don't match",
-    },
-
-    'invalid_card' => {
-      'en_US' => 'Invalid credit card number',
-    },
-
-    'unknown_card_type' => {
-      'en_US' => 'Unknown card type',
-    },
-
-    'not_a' => {
-      'en_US' => 'Not a ',
-    },
-
-    'empty_password' => {
-      'en_US' => 'Empty password',
-    },
-
-    'no_access_number_selected' => {
-      'en_US' => 'No access number selected',
-    },
-
-    'illegal_text' => {
-      'en_US' => 'Illegal (text)',
-      #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in field',
-    },
-
-    'illegal_or_empty_text' => {
-      'en_US' => 'Illegal or empty (text)',
-      #'en_US' => 'Only letters, numbers, spaces, and the following punctuation symbols are permitted: ! @ # $ % & ( ) - + ; : \' " , . ? / in required field',
-    },
-
-    'illegal_username' => {
-      'en_US' => 'Illegal username',
-    },
-
-    'illegal_password' => {
-      'en_US' => 'Illegal password (',
-    },
-
-    'illegal_password_characters' => {
-      'en_US' => ' characters)',
-    },
-
-    'username_in_use' => {
-      'en_US' => 'Username in use',
-    },
-
-    'illegal_email_invoice_address' => {
-      'en_US' => 'Illegal email invoice address',
-    },
-
-    'illegal_name' => {
-      'en_US' => 'Illegal (name)',
-      #'en_US' => 'Only letters, numbers, spaces and the following punctuation symbols are permitted: , . - \' in field',
-    },
-
-    'illegal_phone' => {
-      'en_US' => 'Illegal (phone)',
-      #'en_US' => '',
-    },
-
-    'illegal_zip' => {
-      'en_US' => 'Illegal (zip)',
-      #'en_US' => '',
-    },
-
-    'expired_card' => {
-      'en_US' => 'Expired card',
-    },
-
-    'daytime' => {
-      'en_US' => 'Day Phone',
-    },
-
-    'night' => {
-      'en_US' => 'Night Phone',
-    },
-
-    'svc_external-id' => {
-      'en_US' => 'External ID',
-    },
-
-    'svc_external-title' => {
-      'en_US' => 'Title',
-    },
-
-  );
-}
-
 =back
 
 =head1 BUGS