From f1038a648b3d53db925b23519e7cd2a30c6837ed Mon Sep 17 00:00:00 2001 From: ivan Date: Wed, 20 Mar 2002 21:31:49 +0000 Subject: [PATCH] new export! infostreet and sqlradius provisioning switched over (Bug #299 - doesn't close it, but all the groundwork is done) also removes non-transactional ICRADIUS export from svc_acct.export (closes: Bug#347) --- FS/FS/Conf.pm | 12 +- FS/FS/part_export.pm | 427 +++++++++++++++++++++++++++++++- FS/FS/part_export_option.pm | 2 +- FS/FS/part_svc.pm | 12 +- FS/FS/svc_acct.pm | 236 +++++------------- FS/bin/freeside-queued | 3 +- README.1.4.0pre12 | 11 +- bin/icradius_reset | 7 + bin/svc_acct.export | 99 +------- htetc/global.asa | 2 +- htetc/handler.pl | 2 +- httemplate/docs/export.html | 1 + httemplate/docs/upgrade8.html | 6 + httemplate/edit/part_export.cgi | 130 ++++++++-- httemplate/edit/process/part_export.cgi | 3 + 15 files changed, 654 insertions(+), 299 deletions(-) create mode 100644 bin/icradius_reset diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 91fea7ef7..645dbf1c4 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -396,29 +396,29 @@ httemplate/docs/config.html { 'key' => 'icradiusmachines', - 'section' => 'radius', - 'description' => 'Turn this option on to enable radcheck and radreply table population - by default in the Freeside database, or in the database specified by the icradius_secrets config option (the radcheck and radreply tables needs to be created manually). You do not need to use MySQL for your Freeside database to export to an ICRADIUS/FreeRADIUS MySQL database with this option.
ADDITIONAL DEPRECATED FUNCTIONALITY (instead use MySQL replication or point icradius_secrets to the external database) - your ICRADIUS machines or FreeRADIUS (with MySQL authentication) machines, one per line. Machines listed in this file will have the radcheck table exported to them. Each line should contain four items, separted by whitespace: machine name, MySQL database name, MySQL username, and MySQL password. For example: "radius.isp.tld radius_db radius_user passw0rd"
', + 'section' => 'deprecated', + 'description' => 'DEPRECATED, add sqlradius exports to Service definitions instead. This option used to enable radcheck and radreply table population - by default in the Freeside database, or in the database specified by the icradius_secrets config option (the radcheck and radreply tables needs to be created manually). You do not need to use MySQL for your Freeside database to export to an ICRADIUS/FreeRADIUS MySQL database with this option.
ADDITIONAL DEPRECATED FUNCTIONALITY (instead use MySQL replication or point icradius_secrets to the external database) - your ICRADIUS machines or FreeRADIUS (with MySQL authentication) machines, one per line. Machines listed in this file will have the radcheck table exported to them. Each line should contain four items, separted by whitespace: machine name, MySQL database name, MySQL username, and MySQL password. For example: "radius.isp.tld radius_db radius_user passw0rd"
', 'type' => [qw( checkbox textarea )], }, { 'key' => 'icradius_mysqldest', - 'section' => 'radius', + 'section' => 'deprecated', 'description' => 'DEPRECATED (instead use MySQL replication or point icradius_secrets to the external database) - Destination directory for the MySQL databases, on the ICRADIUS/FreeRADIUS machines. Defaults to "/usr/local/var/".', 'type' => 'text', }, { 'key' => 'icradius_mysqlsource', - 'section' => 'radius', + 'section' => 'deprecated', 'description' => 'DEPRECATED (instead use MySQL replication or point icradius_secrets to the external database) - Source directory for for the MySQL radcheck table files, on the Freeside machine. Defaults to "/usr/local/var/freeside".', 'type' => 'text', }, { 'key' => 'icradius_secrets', - 'section' => 'radius', - 'description' => 'Optionally specifies a database for ICRADIUS/FreeRADIUS export. Three lines: DBI data source, username and password.', + 'section' => 'deprecated', + 'description' => 'DEPRECATED, add sqlradius exports to Service definitions instead. This option used to specify a database for ICRADIUS/FreeRADIUS export. Three lines: DBI data source, username and password.', 'type' => 'textarea', }, diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm index a0de03b63..444e86aa0 100644 --- a/FS/FS/part_export.pm +++ b/FS/FS/part_export.pm @@ -122,6 +122,7 @@ sub insert { my $options = shift; foreach my $optionname ( keys %{$options} ) { my $part_export_option = new FS::part_export_option ( { + 'exportnum' => $self->exportnum, 'optionname' => $optionname, 'optionvalue' => $options->{$optionname}, } ); @@ -144,16 +145,92 @@ Delete this record from the database. =cut -# the delete method can be inherited from FS::Record +#foreign keys would make this much less tedious... grr dumb mysql +sub delete { + my $self = shift; + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $error = $self->SUPER::delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + foreach my $part_export_option ( $self->part_export_option ) { + my $error = $part_export_option->delete; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + + ''; -=item replace OLD_RECORD +} + +=item replace OLD_RECORD HASHREF Replaces the OLD_RECORD with this one in the database. If there is an error, returns the error, otherwise returns false. +If a hash reference of options is supplied, part_export_option records are +created or modified (see L). + =cut -# the replace method can be inherited from FS::Record +sub replace { + my $self = shift; + local $SIG{HUP} = 'IGNORE'; + local $SIG{INT} = 'IGNORE'; + local $SIG{QUIT} = 'IGNORE'; + local $SIG{TERM} = 'IGNORE'; + local $SIG{TSTP} = 'IGNORE'; + local $SIG{PIPE} = 'IGNORE'; + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + my $dbh = dbh; + + my $error = $self->SUPER::replace; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + + my $options = shift; + foreach my $optionname ( keys %{$options} ) { + my $old = qsearchs( 'part_export_option', { + 'exportnum' => $self->exportnum, + 'optionname' => $optionname, + } ); + my $new = new FS::part_export_option ( { + 'exportnum' => $self->exportnum, + 'optionname' => $optionname, + 'optionvalue' => $options->{$optionname}, + } ); + my $error = $old ? $new->replace($old) : $new->insert; + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + + $dbh->commit or die $dbh->errstr if $oldAutoCommit; + + ''; + +}; =item check @@ -198,12 +275,356 @@ sub part_svc { qsearchs('part_svc', { svcpart => $self->svcpart } ); } +=item part_export_option + +=cut + +sub part_export_option { + my $self = shift; + qsearch('part_export_option', { 'exportnum' => $self->exportnum } ); +} + +=item options + +=cut + +sub options { + my $self = shift; + map { $_->optionname => $_->optionvalue } $self->part_export_option; +} + +=item option + +=cut + +sub option { + my $self = shift; + my $part_export_option = + qsearchs('part_export_option', { + exportnum => $self->exportnum, + optionname => shift, + } ); + $part_export_option ? $part_export_option->optionvalue : ''; +} + +=item rebless + +=cut + +sub rebless { + my $self = shift; + my $exporttype = $self->exporttype; + my $class = ref($self); + bless($self, $class."::$exporttype"); +} + +=item export_insert SVC_OBJECT + +Calls the appropriate export_I for this object's exporttype. + +=cut + +sub export_insert { + my $self = shift; + $self->rebless; + $self->_export_insert(@_); +} + +#sub AUTOLOAD { +# my $self = shift; +# $self->rebless; +# my $method = $AUTOLOAD; +# #$method =~ s/::(\w+)$/::_$1/; #infinite loop prevention +# $method =~ s/::(\w+)$/_$1/; #infinite loop prevention +# $self->$method(@_); +#} + +=item export_replace + +=cut + +sub export_replace { + my $self = shift; + $self->rebless; + $self->_export_replace(@_); +} + +=item export_delete + +=cut + +sub export_delete { + my $self = shift; + $self->rebless; + $self->_export_delete(@_); +} + =back +#infostreet + +package FS::part_export::infostreet; +use vars qw(@ISA); +@ISA = qw(FS::part_export); + +sub _export_insert { + my( $self, $svc_acct ) = (shift, shift); + $self->infostreet_queue( $svc_acct->svcnum, + 'createUser', $svc_acct->username, $svc_acct->password ); +} + +sub _export_replace { + my( $self, $new, $old ) = (shift, shift, shift); + return "can't change username with InfoStreet" + if $old->username ne $new->username; + return '' unless $old->_password ne $new->_password; + $self->infostreet_queue( $new->svcnum, + 'passwd', $new->username, $new->password ); +} + +sub _export_delete { + my( $self, $svc_acct ) = (shift, shift); + $self->infostreet_queue( $svc_acct->svcnum, + 'purgeAccount,releaseUsername', $svc_acct->username ); +} + +sub infostreet_queue { + my( $self, $svcnum, $method ) = (shift, shift, shift); + my $queue = new FS::queue { + 'svcnum' => $svcnum, + 'job' => 'FS::part_export::infostreet::infostreet_command', + }; + $queue->insert( + $self->option('url'), + $self->option('login'), + $self->option('password'), + $self->option('groupID'), + $method, + @_, + ); +} + +sub infostreet_command { #subroutine, not method + my($url, $username, $password, $groupID, $method, @args) = @_; + + #quelle hack + if ( $method =~ /,/ ) { + foreach my $part ( split(/,\s*/, $method) ) { + infostreet_command($url, $username, $password, $groupID, $part, @args); + } + return; + } + + eval "use Frontier::Client;"; + + my $conn = Frontier::Client->new( url => $url ); + my $key_result = $conn->call( 'authenticate', $username, $password, $groupID); + my %key_result = _infostreet_parse($key_result); + die $key_result{error} unless $key_result{success}; + my $key = $key_result{data}; + + my $result = $conn->call($opt{method}, $key, @{$opt{args}}); + my %result = _infostreet_parse($result); + die $result{error} unless $result{success}; + +} + +sub _infostreet_parse { #subroutine, not method + my $arg = shift; + map { + my $value = $arg->{$_}; + #warn ref($value); + $value = $value->value() + if ref($value) && $value->isa('Frontier::RPC2::DataType'); + $_=>$value; + } keys %$arg; +} + +#sqlradius + +package FS::part_export::sqlradius; +use vars qw(@ISA); +@ISA = qw(FS::part_export); + +sub _export_insert { + my($self, $svc_acct) = (shift, shift); + $self->sqlradius_queue( $svc_acct->svcnum, 'insert', + 'reply', $svc_acct->username, $svc_acct->radius_reply ); + $self->sqlradius_queue( $svc_acct->svcnum, 'insert', + 'check', $svc_acct->username, $svc_acct->radius_check ); +} + +sub _export_replace { + my( $self, $new, $old ) = (shift, shift, shift); + + #return "can't (yet) change username with sqlradius" + # if $old->username ne $new->username; + if ( $old->username ne $new->username ) { + my $error = $self->sqlradius_queue( $new->svcnum, 'rename', + $new->username, $old->username ); + return $error if $error; + } + + foreach my $table (qw(reply check)) { + my $method = "radius_$table"; + my %new = $new->$method; + my %old = $old->$method; + if ( grep { !exists $old{$_} #new attributes + || $new{$n} ne $old{$n} #changed + } keys %new + ) { + my $error = $self->sqlradius_queue( $new->svcnum, 'insert' + $table, $new->username, %new ); + return $error if $error; + } + + my @del = grep { !exists $new{$_} } keys %old; + my $error = $self->sqlradius_queue( $new->svcnum, 'sqlradius_attrib_delete', + $table, $new->username, @del ); + return $error if $error; + } + + ''; +} + +sub _export_delete { + my( $self, $svc_something ) = (shift, shift); + $self->sqlradius_queue( $svc_acct->svcnum, 'delete', + $svc_something->username ); +} + +sub sqlradius_queue { + my( $self, $svcnum, $method ) = (shift, shift, shift); + my $queue = new FS::queue { + 'svcnum' => $svcnum, + 'job' => "FS::part_export::sqlradius::sqlradius_$method", + }; + $queue->insert( + $self->option('datasrc'), + $self->option('username'), + $self->option('password'), + @_, + ); +} + +sub sqlradius_insert { #subroutine, not method + my $dbh = sqlradius_connect(shift, shift, shift); + my( $replycheck, $username, %attributes ) = @_; + + foreach my $attribute ( keys %attributes ) { + my $u_sth = $dbh->prepare( + "UPDATE rad$replycheck SET Value = ? WHERE UserName = ? AND Attribute = ?" ) or die $dbh->errstr; + my $i_sth = $dbh->prepare( + "INSERT INTO rad$replycheck ( id, UserName, Attribute, Value ) ". + "VALUES ( ?, ?, ?, ? )" ) + or die $dbh->errstr; + $u_sth->execute($attributes{$attribute}, $username, $attribute) > 0 + or $i_sth->execute( '', $username, $attribute, $attributes{$attribute} ) + or die "can't insert into rad$replycheck table: ". $i_sth->errstr; + } + $dbh->disconnect; +} + +sub sqlradius_rename { #subroutine, not method + my $dbh = sqlradius_connect(shift, shift, shift); + my($new_username, $old_username) = @_; + foreach my $table (qw(radreply radcheck)) { + my $sth = $dbh->prepare("UPDATE $table SET Username = ? WHERE UserName = ?") + or die $dbh->errstr; + $sth->execute($new_username, $old_username) + or die "can't update $table: ". $sth->errstr; + } + $dbh->disconnect; +} + +sub sqlradius_attrib_delete { #subroutine, not method + my $dbh = sqlradius_connect(shift, shift, shift); + my( $replycheck, $username, @attrib ) = @_; + + foreach my $attribute ( @attrib ) { + my $sth = $dbh->prepare( + "DELETE FROM $table WHERE UserName = ? AND Attribute = ?" ) + or die $dbh->errstr; + $sth->execute($username,$attribute) + or die "can't delete from $table table: ". $sth->errstr; + } + $dbh->disconnect; +} + +sub sqlradius_delete { #subroutine, not method + my $dbh = sqlradius_connect(shift, shift, shift); + my $username = shift; + + foreach my $table (qw( radcheck radreply )) { + my $sth = $dbh->prepare( "DELETE FROM $table WHERE UserName = ?" ); + $sth->execute($username) + or die "can't delete from $table table: ". $sth->errstr; + } + $dbh->disconnect; +} + +sub sqlradius_connect { + #my($datasrc, $username, $password) = @_; + #DBI->connect($datasrc, $username, $password) or die $DBI::errstr; + DBI->connect(@_) or die $DBI::errstr; +} + +=head1 NOTES + +Writing a new export class: + +#myexport + +package FS::part_export::myexport; +use vars qw(@ISA); +@ISA = qw(FS::part_export); + +sub _export_insert { + my($self, $svc_something) = (shift, shift); + $self->myexport_queue( $svc_acct->svcnum, 'insert', + $svc_something->username, $svc_something->password ); +} + +sub _export_replace { + my( $self, $new, $old ) = (shift, shift, shift); + #return "can't change username with myexport" + # if $old->username ne $new->username; + #return '' unless $old->_password ne $new->_password; + $self->myexport_queue( $new->svcnum, + 'replace', $new->username, $new->password ); +} + +sub _export_delete { + my( $self, $svc_something ) = (shift, shift); + $self->myexport_queue( $svc_acct->svcnum, + 'delete', $svc_something->username ); +} + +#a good idea to queue anything that could fail or take any time +sub myexport_queue { + my( $self, $svcnum, $method ) = (shift, shift, shift); + my $queue = new FS::queue { + 'svcnum' => $svcnum, + 'job' => "FS::part_export::myexport::myexport_$method", + }; + $queue->insert( @_ ); +} + +sub myexport_insert { #subroutine, not method +} +sub myexport_replace { #subroutine, not method +} +sub myexport_delete { #subroutine, not method +} + =head1 BUGS Probably. +Hmm, export code has wound up in here. Move those sub-classes out into their +own files, at least. Also hmm... cust_export class (not necessarily a +database table...) ... ? + =head1 SEE ALSO L, L, L, L, diff --git a/FS/FS/part_export_option.pm b/FS/FS/part_export_option.pm index 1ce0de65f..61ea956ae 100644 --- a/FS/FS/part_export_option.pm +++ b/FS/FS/part_export_option.pm @@ -40,7 +40,7 @@ currently supported: =item optionname - option name -=item opeionvalue - option value +=item optionvalue - option value =back diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm index 41ee21d31..82935dd2b 100644 --- a/FS/FS/part_svc.pm +++ b/FS/FS/part_svc.pm @@ -295,11 +295,21 @@ sub all_part_svc_column { qsearch('part_svc_column', { 'svcpart' => $self->svcpart } ); } +=item part_export + +=cut + +sub part_export { + my $self = shift; + my %search = ( 'svcpart' => $self->svcpart ); + qsearch('part_export', \%search); +} + =back =head1 VERSION -$Id: part_svc.pm,v 1.9 2002-01-28 06:57:23 ivan Exp $ +$Id: part_svc.pm,v 1.10 2002-03-20 21:31:49 ivan Exp $ =head1 BUGS diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index bb9fe67b3..2305aeb80 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -1,7 +1,8 @@ package FS::svc_acct; use strict; -use vars qw( @ISA $nossh_hack $conf $dir_prefix @shells $usernamemin +use vars qw( @ISA $nossh_hack $noexport_hack $conf + $dir_prefix @shells $usernamemin $usernamemax $passwordmin $passwordmax $username_ampersand $username_letter $username_letterfirst $username_noperiod $username_uppercase @@ -9,7 +10,6 @@ use vars qw( @ISA $nossh_hack $conf $dir_prefix @shells $usernamemin $cyrus_server $cyrus_admin_user $cyrus_admin_pass $cp_server $cp_user $cp_pass $cp_workgroup $dirhash - $icradius_dbh @saltset @pw_set $rsync $ssh $exportdir $vpopdir); use Carp; @@ -88,18 +88,7 @@ $FS::UID::callback{'FS::svc_acct'} = sub { $cp_pass = ''; $cp_workgroup = ''; } - if ( $conf->exists('icradiusmachines') ) { - if ( $conf->exists('icradius_secrets') ) { - #need some sort of late binding so it's only connected to when - # actually used, hmm - $icradius_dbh = DBI->connect($conf->config('icradius_secrets')) - or die $DBI::errstr; - } else { - $icradius_dbh = dbh; - } - } else { - $icradius_dbh = ''; - } + $dirhash = $conf->config('dirhash') || 0; $exportdir = "/usr/local/etc/freeside/export." . datasrc; if ( $conf->exists('vpopmailmachines') ) { @@ -246,6 +235,8 @@ $username, $uid, $gid, $dir, and $shell. (TODOC: cyrus config file, L and L) +(TODOC: new exports! $noexport_hack) + =cut sub insert { @@ -297,6 +288,20 @@ sub insert { return $error; } + #new-style exports! + unless ( $noexport_hack ) { + foreach my $part_export ( $self->cust_svc->part_svc->part_export ) { + my $error = $part_export->export_insert($self); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "exporting to ". $part_export->exporttype. + " (transaction rolled back): $error"; + } + } + } + + #old-style exports + my( $username, $uid, $gid, $dir, $shell ) = ( $self->username, $self->uid, @@ -340,37 +345,6 @@ sub insert { } } - if ( $icradius_dbh ) { - - my $radcheck_queue = - new FS::queue { - 'svcnum' => $self->svcnum, - 'job' => 'FS::svc_acct::icradius_rc_insert' - }; - $error = $radcheck_queue->insert( $self->username, - $self->_password, - $self->radius_check - ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } - - my $radreply_queue = - new FS::queue { - 'svcnum' => $self->svcnum, - 'job' => 'FS::svc_acct::icradius_rr_insert' - }; - $error = $radreply_queue->insert( $self->username, - $self->_password, - $self->radius_reply - ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } - } - if ( $vpopdir ) { my $vpopmail_queue = @@ -390,6 +364,7 @@ sub insert { } + #end of old-style exports $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; #no error @@ -451,56 +426,6 @@ sub cp_insert { die $app->message."\n" unless $app->ok; } -sub icradius_rc_insert { - my( $username, $password, %radcheck ) = @_; - - my $sth = $icradius_dbh->prepare( - "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". - join(", ", map { $icradius_dbh->quote($_) } ( - '', - $username, - "Password", - $password, - ) ). " )" - ); - $sth->execute or die "can't insert into radcheck table: ". $sth->errstr; - - foreach my $attribute ( keys %radcheck ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". - join(", ", map { $icradius_dbh->quote($_) } ( - '', - $username, - $attribute, - $radcheck{$attribute}, - ) ). " )" - ); - $sth->execute or die "can't insert into radcheck table: ". $sth->errstr; - } - - 1; -} - -sub icradius_rr_insert { - my( $username, $password, %radreply ) = @_; - - foreach my $attribute ( keys %radreply ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radreply ( id, UserName, Attribute, Value ) VALUES ( ". - join(", ", map { $icradius_dbh->quote($_) } ( - '', - $username, - $attribute, - $radreply{$attribute}, - ) ). " )" - ); - $sth->execute or die "can't insert into radreply table: ". $sth->errstr; - } - - 1; -} - - sub vpopmail_insert { my( $username, $password, $domain, $vpopdir ) = @_; @@ -571,6 +496,8 @@ $username and $dir. (TODOC: cyrus config file) +(TODOC: new exports! $noexport_hack) + =cut sub delete { @@ -590,7 +517,7 @@ sub delete { return "Can't delete an account with (svc_www) web service!" if qsearch( 'svc_www', { 'usersvc' => $self->usersvc } ); - # what about records in session ? + # what about records in session ? (they should refer to history table) local $SIG{HUP} = 'IGNORE'; local $SIG{INT} = 'IGNORE'; @@ -639,6 +566,20 @@ sub delete { return $error; } + #new-style exports! + unless ( $noexport_hack ) { + foreach my $part_export ( $self->cust_svc->part_svc->part_export ) { + my $error = $part_export->export_delete($self); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "exporting to ". $part_export->exporttype. + " (transaction rolled back): $error"; + } + } + } + + #old-style exports + my( $username, $dir ) = ( $self->username, $self->dir, @@ -671,24 +612,6 @@ sub delete { } } - if ( $icradius_dbh ) { - - my $radcheck_queue = - new FS::queue { 'job' => 'FS::svc_acct::icradius_rc_delete' }; - $error = $radcheck_queue->insert( $self->username ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } - - my $radreply_queue = - new FS::queue { 'job' => 'FS::svc_acct::icradius_rr_delete' }; - $error = $radreply_queue->insert( $self->username ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } - } if ( $vpopdir ) { my $queue = new FS::queue { 'job' => 'FS::svc_acct::vpopmail_delete' }; $error = $queue->insert( $self->username, $self->domain ); @@ -699,6 +622,8 @@ sub delete { } + #end of old-style exports + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; } @@ -742,30 +667,6 @@ sub cp_delete { die $app->message."\n" unless $app->ok; } -sub icradius_rc_delete { - my $username = shift; - - my $sth = $icradius_dbh->prepare( - 'DELETE FROM radcheck WHERE UserName = ?' - ); - $sth->execute($username) - or die "can't delete from radcheck table: ". $sth->errstr; - - 1; -} - -sub icradius_rr_delete { - my $username = shift; - - my $sth = $icradius_dbh->prepare( - 'DELETE FROM radreply WHERE UserName = ?' - ); - $sth->execute($username) - or die "can't delete from radreply table: ". $sth->errstr; - - 1; -} - sub vpopmail_delete { my( $username, $domain ) = @_; @@ -858,6 +759,20 @@ sub replace { return $error if $error; } + #new-style exports! + unless ( $noexport_hack ) { + foreach my $part_export ( $new->cust_svc->part_svc->part_export ) { + my $error = $part_export->export_replace($new,$old); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return "exporting to ". $part_export->exporttype. + " (transaction rolled back): $error"; + } + } + } + + #old-style exports + my ( $old_dir, $new_dir, $uid, $gid ) = ( $old->getfield('dir'), $new->getfield('dir'), @@ -900,19 +815,6 @@ sub replace { } } - if ( $icradius_dbh ) { - my $queue = new FS::queue { - 'svcnum' => $new->svcnum, - 'job' => 'FS::svc_acct::icradius_rc_replace' - }; - $error = $queue->insert( $new->username, - $new->_password, - ); - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "queueing job (transaction rolled back): $error"; - } - } if ( $vpopdir ) { my $cpassword = crypt( $new->_password,$saltset[int(rand(64))].$saltset[int(rand(64))] @@ -938,23 +840,12 @@ sub replace { } } + #end of old-style exports $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; #no error } -sub icradius_rc_replace { - my( $username, $new_password ) = @_; - - my $sth = $icradius_dbh->prepare( - "UPDATE radcheck SET Value = ? WHERE UserName = ? and Attribute = ?" - ); - $sth->execute($new_password, $username, 'Password' ) - or die "can't update radcheck table: ". $sth->errstr; - - 1; -} - sub cp_rename { my ( $old_username, $new_username ) = @_; @@ -1305,19 +1196,22 @@ sub radius_reply { Returns key/value pairs, suitable for assigning to a hash, for any RADIUS check attributes of this record. -Accessing RADIUS attributes directly is not supported and will break in the -future. +Note that this is now the preferred method for reading RADIUS attributes - +accessing the columns directly is discouraged, as the column names are +expected to change in the future. =cut sub radius_check { my $self = shift; - map { - /^(rc_(.*))$/; - my($column, $attrib) = ($1, $2); - #$attrib =~ s/_/\-/g; - ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) ); - } grep { /^rc_/ && $self->getfield($_) } fields( $self->table ); + ( 'Password' => $self->_password, + map { + /^(rc_(.*))$/; + my($column, $attrib) = ($1, $2); + #$attrib =~ s/_/\-/g; + ( $FS::raddb::attrib{lc($attrib)}, $self->getfield($column) ); + } grep { /^rc_/ && $self->getfield($_) } fields( $self->table ) + ); } =item domain diff --git a/FS/bin/freeside-queued b/FS/bin/freeside-queued index 56475d059..c3c9240d2 100644 --- a/FS/bin/freeside-queued +++ b/FS/bin/freeside-queued @@ -15,6 +15,7 @@ use FS::queue; use FS::cust_main; use FS::svc_acct; use Net::SSH 0.05; +use FS::part_export; my $pid_file = '/var/run/freeside-queued.pid'; @@ -85,7 +86,7 @@ while (1) { my $eval = "&". $ljob->job. '(@args);'; warn "running $eval"; - eval $eval; + eval $eval; #throw away return value? suppose so if ( $@ ) { warn "job $eval failed"; my %hash = $ljob->hash; diff --git a/README.1.4.0pre12 b/README.1.4.0pre12 index c801a2604..d61656c38 100644 --- a/README.1.4.0pre12 +++ b/README.1.4.0pre12 @@ -21,9 +21,16 @@ Run bin/create-history-tables Run bin/dbdef-create again +the mxmachines, nsmachines, arecords and cnamerecords configuration values have been deprecated. Use the defaultrecords configuration value instead. + +New export code has landed! If you were using the icradiusmachines, +icradius_mysqldest, icradius_mysqlsource, or icradius_secrets files, see +the "sqlradius" export instead. Use MySQL replication + +or point the "sqlradius" export directly at your external ICRADIUS or +FreeRADIUS (or through an SSL-encrypting proxy...) + Arrange for freeside-expiration-alerter to be run daily, if desired. Restart Apache and freeside-queued -the mxmachines, nsmachines, arecords and cnamerecords configuration values have been deprecated. Use the defaultrecords configuration value instead. - diff --git a/bin/icradius_reset b/bin/icradius_reset new file mode 100644 index 000000000..58663ee6b --- /dev/null +++ b/bin/icradius_reset @@ -0,0 +1,7 @@ + + my $sth = $icradius_dbh->prepare("DELETE FROM radcheck"); + $sth->execute or die "Can't reset radcheck table: ". $sth->errstr; + my $sth2 = $icradius_dbh->prepare("DELETE FROM radreply"); + $sth2->execute or die "Can't reset radreply table: ". $sth2->errstr; + + diff --git a/bin/svc_acct.export b/bin/svc_acct.export index 82a8935b6..261f499da 100755 --- a/bin/svc_acct.export +++ b/bin/svc_acct.export @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Id: svc_acct.export,v 1.34 2002-02-23 02:14:26 jeff Exp $ +# $Id: svc_acct.export,v 1.35 2002-03-20 21:31:49 ivan Exp $ # # Create and export password, radius and vpopmail password files: # passwd, passwd.adjunct, shadow, acp_passwd, acp_userinfo, acp_dialup @@ -47,22 +47,6 @@ my @erpcdmachines = $conf->config('erpcdmachines') my @radiusmachines = $conf->config('radiusmachines') if $conf->exists('radiusmachines'); -my $icradiusmachines = $conf->exists('icradiusmachines'); -my @icradiusmachines = $conf->config('icradiusmachines') if $icradiusmachines; -my $icradius_mysqldest = - $conf->config('icradius_mysqldest') || "/usr/local/var" - if $icradiusmachines; -my $icradius_mysqlsource = - $conf->config('icradius_mysqlsource') || "/usr/local/var/freeside" - if $icradiusmachines; -my $icradius_dbh; -if ( $icradiusmachines && $conf->exists('icradius_secrets') ) { - $icradius_dbh = DBI->connect($conf->config('icradius_secrets')) - or die $DBI::errstr; -} else { - $icradius_dbh = dbh; -} - my $textradiusprepend = $conf->exists('textradiusprepend') ? $conf->config('textradiusprepend') @@ -189,13 +173,6 @@ chmod 0600, "$spooldir/master.passwd", rmtree"$spooldir/domains", 0, 1; mkdir "$spooldir/domains", 0700; -if ( $icradiusmachines ) { - my $sth = $icradius_dbh->prepare("DELETE FROM radcheck"); - $sth->execute or die "Can't reset radcheck table: ". $sth->errstr; - my $sth2 = $icradius_dbh->prepare("DELETE FROM radreply"); - $sth2->execute or die "Can't reset radreply table: ". $sth2->errstr; -} - setpriority(0,0,10); print USERS "$radiusprepend\n"; @@ -399,7 +376,7 @@ foreach $svc_domain (sort {$a->domain cmp $b->domain} @svc_domain) { $username, qq(\t${textradiusprepend}), $radcheck, - qq(Password = "$rpassword"\n\t), +# qq(Password = "$rpassword"\n\t), join ",\n\t", map { qq($_ = "$radreply{$_}") } keys %radreply; if ( $ip && $ip ne '0e0' ) { @@ -409,57 +386,6 @@ foreach $svc_domain (sort {$a->domain cmp $b->domain} @svc_domain) { print USERS qq(\n\n); } - ### - # ICRADIUS export - if ( $icradiusmachines ) { - - my $sth = $icradius_dbh->prepare( - "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". - join(", ", map { $icradius_dbh->quote( $_ ) } ( - '', - $username, - "Password", - $svc_acct->_password, - ) ). " )" - ); - $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr; - - foreach my $attribute ( keys %radcheck ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radcheck ( id, UserName, Attribute, Value ) VALUES ( ". - join(", ", map { $icradius_dbh->quote( $_ ) } ( - '', - $username, - $attribute, - $radcheck{$attribute}, - ) ). " )" - ); - $sth->execute or die "Can't insert into radcheck table: ". $sth->errstr; } - - foreach my $attribute ( keys %radreply ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radreply (id, UserName, Attribute, Value) VALUES ( ". - join(", ", map { $icradius_dbh->quote( $_ ) } ( - '', - $username, - $attribute, - $radreply{$attribute}, - ) ). " )" - ); - $sth->execute or die "Can't insert into radreply table: ". $sth->errstr; } - - if ( $ip && $ip ne '0e0' ) { - my $sth = $icradius_dbh->prepare( - "INSERT INTO radreply (id, UserName, Attribute, Value) VALUES ( ". - join(", ", map { $icradius_dbh->quote( $_ ) } ( - '', - $username, - 'Framed-IP-Address', - $ip, - ) ). " )" - ); - $sth->execute or die "Can't insert into radreply table: ". $sth->errstr; } - } } ### @@ -629,27 +555,6 @@ foreach $radiusmachine (@radiusmachines) { == 0 or die "ssh error: $!"; } -foreach my $icradiusmachine ( @icradiusmachines ) { - my( $machine, $db, $user, $pass ) = split(/\s+/, $icradiusmachine); - chdir $icradius_mysqlsource or die "Can't cd $icradius_mysqlsource: $!"; - open(WRITER,"|ssh root\@$machine mysql -v --user=$user -p $db"); - my $oldfh = select WRITER; $|=1; select $oldfh; - print WRITER "$pass\n"; - sleep 2; - print WRITER "LOCK TABLES radcheck WRITE, radreply WRITE;\n"; - foreach my $file ( glob("radcheck.*") ) { - my $scp = new Net::SCP; - $scp->scp($file,"root\@$machine:$icradius_mysqldest/$db/$file") - or die "scp error: ". $scp->{errstr}; - } - foreach my $file ( glob("radreply.*") ) { - my $scp = new Net::SCP; - $scp->scp($file,"root\@$machine:$icradius_mysqldest/$db/$file") - or die "scp error: ". $scp->{errstr}; - } - close WRITER; -} - #my @args = ("/bin/tar", "c", "--force-local", "-C", "$spooldir", "-f", "$spooldir/vpoptarball", "domains"); #system {$args[0]} @args; diff --git a/htetc/global.asa b/htetc/global.asa index 083f2253d..94d6b7e3e 100644 --- a/htetc/global.asa +++ b/htetc/global.asa @@ -47,7 +47,7 @@ use FS::svc_forward; use FS::svc_www; use FS::type_pkgs; use FS::part_export; -#use FS::part_export_option; +use FS::part_export_option; sub Script_OnStart { $Response->AddHeader('Pragma' => 'no-cache'); diff --git a/htetc/handler.pl b/htetc/handler.pl index 5f244921e..8e097c656 100644 --- a/htetc/handler.pl +++ b/htetc/handler.pl @@ -104,7 +104,7 @@ sub handler use FS::svc_www; use FS::type_pkgs; use FS::part_export; - #use FS::part_export_option; + use FS::part_export_option; *CGI::redirect = sub { my( $self, $location ) = @_; diff --git a/httemplate/docs/export.html b/httemplate/docs/export.html index c7f1b4c9e..71e3acf1f 100755 --- a/httemplate/docs/export.html +++ b/httemplate/docs/export.html @@ -3,6 +3,7 @@

File exporting

+ NOTE: This file is OUT OF DATE with the landing of the new export code and is only here for reference. DO NOT follow these instructions. Instead use the new exports in the web interface.
  • bin/svc_acct.export will create UNIX passwd, shadow and master.passwd files, ERPCD acp_passwd and acp_dialup files and a RADIUS users file in the /usr/local/etc/freeside/export.datasrc directory. Some RADIUS servers (such as Radiator, ICRADIUS and FreeRADIUS) will authenticate directly out of an SQL database. In these cases, it is reccommended that you replicate (Replication in MySQL) the data to an external RADIUS machine or point icradius_secrets to the external machine rather than running the RADIUS server on your Freeside machine. Using the appropriate configuration settings, you can export these files to your remote machines unattended: diff --git a/httemplate/docs/upgrade8.html b/httemplate/docs/upgrade8.html index 25e766b1e..e52b69ad3 100644 --- a/httemplate/docs/upgrade8.html +++ b/httemplate/docs/upgrade8.html @@ -319,5 +319,11 @@ ALTER TABLE cust_refund DROP COLUMN crednum; freeside-expiration-alerter to be run daily by cron or similar facility. The message it sends can be configured from the Configuration choice of the main menu as alerter_template. +
  • Export has been rewritten. If you were using the icradiusmachines, + icradius_mysqldest, icradius_mysqlsource, or icradius_secrets files, add + an appropriate "sqlradius" export to all relevant Service Definitions + instead. Use MySQL replication or + point the "sqlradius" export directly at your external ICRADIUS or FreeRADIUS + database (or through an SSL-necrypting proxy...)
diff --git a/httemplate/edit/part_export.cgi b/httemplate/edit/part_export.cgi index ffce0438f..a44bd39d5 100644 --- a/httemplate/edit/part_export.cgi +++ b/httemplate/edit/part_export.cgi @@ -22,7 +22,7 @@ if ( $cgi->param('error') ) { } ); } -warn "***$query***"; +#warn "***$query***"; if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) { $action = 'Add'; my $old_part_export = qsearchs('part_export', { 'exportnum' => $1 } ); @@ -37,7 +37,112 @@ if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) { } $action ||= $part_export->exportnum ? 'Edit' : 'Add'; -my @types = qw(shell bsdshell textradius sqlradius cp); +my %exports = ( + 'svc_acct' => { + 'sysvshell' => { + 'desc' => + 'Batch export of /etc/passwd and /etc/shadow files (Linux/SysV)', + 'options' => {}, + }, + 'bsdshell' => { + 'desc' => + 'Batch export of /etc/passwd and /etc/master.passwd files (BSD)', + 'options' => {}, + }, +# 'nis' => { +# 'desc' => +# 'Batch export of /etc/global/passwd and /etc/global/shadow for NIS ', +# 'options' => {}, +# }, + 'bsdshell' => { + 'desc' => + 'Batch export of /etc/passwd and /etc/master.passwd files (BSD)', + 'options' => {}, + }, + 'textradius' => { + 'desc' => 'Batch export of a text /etc/raddb/users file (Livingston, Cistron)', + }, + 'sqlradius' => { + 'desc' => 'Real-time export to SQL-backed RADIUS (ICRADIUS, FreeRADIUS)', + 'options' => { + 'datasrc' => { label=>'DBI data source' }, + 'username' => { label=>'Database username' }, + 'password' => { label=>'Database password' }, + }, + 'nodomain' => 'Y', + 'notes' => 'Not specifying datasrc will export to the freeside database? (no... notes on MySQL replication, etc., from Conf.pm && export.html etc.', + }, + 'cyrus' => { + 'desc' => 'Real-time export to Cyrus IMAP server', + }, + 'cp' => { + 'desc' => 'Real-time export to Critical Path Account Provisioning Protocol', + }, + 'infostreet' => { + 'desc' => 'Real-time export to InfoStreet streetSmartAPI', + 'options' => { + 'url' => { label=>'XML-RPC Access URL', }, + 'login' => { label=>'InfoStreet login', }, + 'password' => { label=>'InfoStreet password', }, + 'groupID' => { label=>'InfoStreet groupID', }, + }, + 'nodomain' => 'Y', + 'notes' => 'http://www.infostreet.com/ .... install Frontier::Client', + } + }, + + 'svc_domain' => {}, + + 'svc_acct_sm' => {}, + + 'svc_forward' => {}, + + 'svc_www' => {}, + +); + +my $svcdb = $part_export->part_svc->svcdb; +my %layers = map { $_ => "$_ - ". $exports{$svcdb}{$_}{desc} } + keys %{$exports{$svcdb}}; +$layers{''}=''; + +my $widget = new HTML::Widgets::SelectLayers( + 'selected_layer' => $part_export->exporttype, + 'selected_layer' => $part_export->exporttype, + 'options' => \%layers, + 'form_name' => 'dummy', + 'form_action' => 'process/part_export.cgi', + 'form_text' => [qw( exportnum svcpart machine )], +# 'form_checkbox' => [qw()], + 'html_between' => "\n", + 'layer_callback' => sub { + my $layer = shift; + my $html = qq!!. + ntable("#cccccc",2); + foreach my $option ( keys %{$exports{$svcdb}->{$layer}{options}} ) { +# foreach my $option ( qw(url login password groupID ) ) { + my $optinfo = $exports{$svcdb}->{$layer}{options}{$option}; + my $label = $optinfo->{label}; + my $value = $part_export->option($option); + $html .= qq!$label!. + qq!!. + ''; + } + $html .= ''; + + $html .= ''; + + $html .= ''; + + $html .= ''; + + $html; + }, +); %> <%= header("$action Export", menubar( @@ -46,35 +151,30 @@ my @types = qw(shell bsdshell textradius sqlradius cp); %> <% if ( $cgi->param('error') ) { %> -Error: <%= $cgi->param('error') %> + Error: <%= $cgi->param('error') %> +

<% } %> -
-<% #print ''; %> + + <%= ntable("#cccccc",2) %> Service <%= $part_export->svcpart %> - <%= $part_export->part_svc->svc %> + - Export - - - Export host - -
+ + Export + <%= $widget->html %> diff --git a/httemplate/edit/process/part_export.cgi b/httemplate/edit/process/part_export.cgi index 9ee1c5974..8160527f6 100644 --- a/httemplate/edit/process/part_export.cgi +++ b/httemplate/edit/process/part_export.cgi @@ -6,6 +6,9 @@ my $exportnum = $cgi->param('exportnum'); my $old = qsearchs('part_export', { 'exportnum'=>$exportnum } ) if $exportnum; +#fixup options +my %options = map { $_=>$cgi->param($_) } $cgi->param('options'); + my $new = new FS::part_export ( { map { $_, scalar($cgi->param($_)); -- 2.11.0