diff options
author | mark <mark> | 2011-11-10 21:40:05 +0000 |
---|---|---|
committer | mark <mark> | 2011-11-10 21:40:05 +0000 |
commit | 307a7d85568a15f5eb0d97c648507484108fcc56 (patch) | |
tree | 22e4da31ad00e299cea5dbf983234f839f17225b /FS | |
parent | cfbfa38f73888ee2c073ad7500c1fe147cde1c81 (diff) |
RADIUS groups for svc_broadband, #14695
Diffstat (limited to 'FS')
-rw-r--r-- | FS/FS/Conf.pm | 7 | ||||
-rw-r--r-- | FS/FS/nas.pm | 7 | ||||
-rw-r--r-- | FS/FS/part_export.pm | 7 | ||||
-rw-r--r-- | FS/FS/part_export/broadband_sqlradius.pm | 100 | ||||
-rw-r--r-- | FS/FS/part_export/phone_sqlradius.pm | 1 | ||||
-rw-r--r-- | FS/FS/part_svc.pm | 16 | ||||
-rw-r--r-- | FS/FS/radius_usergroup.pm | 14 | ||||
-rw-r--r-- | FS/FS/svc_Radius_Mixin.pm | 94 | ||||
-rw-r--r-- | FS/FS/svc_acct.pm | 137 | ||||
-rwxr-xr-x | FS/FS/svc_broadband.pm | 11 | ||||
-rwxr-xr-x | FS/bin/freeside-sqlradius-reset | 7 |
11 files changed, 253 insertions, 148 deletions
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 4104fda..8d26e91 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2297,6 +2297,13 @@ and customer address. Include units.', }, { + 'key' => 'svc_broadband-radius', + 'section' => '', + 'description' => 'Enable RADIUS groups for broadband services.', + 'type' => 'checkbox', + }, + + { 'key' => 'svc_acct-alldomains', 'section' => '', 'description' => 'Allow accounts to select any domain in the database. Normally accounts can only select from the domain set in the service definition and those purchased by the customer.', diff --git a/FS/FS/nas.pm b/FS/FS/nas.pm index 4564a63..af5a23a 100644 --- a/FS/FS/nas.pm +++ b/FS/FS/nas.pm @@ -87,8 +87,11 @@ sub delete { my $dbh = dbh; my $self = shift; - my $error = $self->process_m2m([]) - || $self->SUPER::delete; + my $error = $self->process_m2m( + link_table => 'export_nas', + target_table => 'part_export', + params => [] + ) || $self->SUPER::delete; if ( $error ) { $dbh->rollback; diff --git a/FS/FS/part_export.pm b/FS/FS/part_export.pm index f84f2a0..4b60953 100644 --- a/FS/FS/part_export.pm +++ b/FS/FS/part_export.pm @@ -128,7 +128,12 @@ sub delete { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - my $error = $self->SUPER::delete; + # clean up export_nas records + my $error = $self->process_m2m( + 'link_table' => 'export_nas', + 'target_table' => 'nas', + 'params' => [], + ) || $self->SUPER::delete; if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; diff --git a/FS/FS/part_export/broadband_sqlradius.pm b/FS/FS/part_export/broadband_sqlradius.pm new file mode 100644 index 0000000..ae0876d --- /dev/null +++ b/FS/FS/part_export/broadband_sqlradius.pm @@ -0,0 +1,100 @@ +package FS::part_export::broadband_sqlradius; + +use strict; +use vars qw($DEBUG @ISA %options %info $conf); +use Tie::IxHash; +use FS::Conf; +use FS::Record qw( dbh str2time_sql ); #qsearch qsearchs ); +use FS::part_export::sqlradius qw(sqlradius_connect); + +FS::UID->install_callback(sub { $conf = new FS::Conf }); + +@ISA = qw(FS::part_export::sqlradius); + +$DEBUG = 0; + +tie %options, 'Tie::IxHash', + 'datasrc' => { label=>'DBI data source ' }, + 'username' => { label=>'Database username' }, + 'password' => { label=>'Database password' }, + 'usergroup'=> { label => 'Group table', + type => 'select', + options => [qw( radusergroup usergroup )], + }, +# session report doesn't currently know about this export anyway +# 'hide_ip' => { +# type => 'checkbox', +# label => 'Hide IP address on session reports', +# }, + 'mac_as_password' => { + type => 'checkbox', + default => '1', + label => 'Use MAC address as password', + }, + 'radius_password' => { label=>'Fixed password' }, + 'ip_addr_as' => { label => 'Send IP address as', + default => 'Framed-IP-Address' }, +; + +%info = ( + 'svc' => 'svc_broadband', + 'desc' => 'Real-time export to SQL-backed RADIUS (such as FreeRadius) for broadband services', + 'options' => \%options, + 'nas' => 'Y', + 'notes' => <<END, +Real-time export of <b>radcheck</b>, <b>radreply</b>, and <b>usergroup</b> +tables to any SQL database for +<a href="http://www.freeradius.org/">FreeRADIUS</a> +or <a href="http://radius.innercite.com/">ICRADIUS</a>. +<br><br> + +This export is for broadband service access control based on MAC address. +For a more typical RADIUS export, see sqlradius. +<br><br> + +See the +<a href="http://search.cpan.org/dist/DBI/DBI.pm#connect">DBI documentation</a> +and the +<a href="http://search.cpan.org/search?mode=module&query=DBD%3A%3A">documentation for your DBD</a> +for the exact syntax of a DBI data source. + +END +); + +sub rebless { shift; } + +sub export_username { + my($self, $svc_broadband) = (shift, shift); + $svc_broadband->mac_addr; +} + +sub radius_reply { + my($self, $svc_broadband) = (shift, shift); + my %reply; + if ( length($self->option('ip_addr_as',1)) + and length($svc_broadband->ip_addr) ) { + $reply{$self->option('ip_addr_as')} = $svc_broadband->ip_addr; + } + %reply; +} + +sub radius_check { + my($self, $svc_broadband) = (shift, shift); + my $password_attrib = $conf->config('radius-password') || 'Password'; + my %check; + if ( $self->option('mac_as_password') ) { + $check{$password_attrib} = $svc_broadband->mac_addr; #formatting? + } + elsif ( length( $self->option('radius_password',1)) ) { + $check{$password_attrib} = $self->option('radius_password'); + } + %check; +} + +sub _export_suspend {} +sub _export_unsuspend {} + +sub update_svc {} #do nothing + +1; + diff --git a/FS/FS/part_export/phone_sqlradius.pm b/FS/FS/part_export/phone_sqlradius.pm index 0816efa..6b14bed 100644 --- a/FS/FS/part_export/phone_sqlradius.pm +++ b/FS/FS/part_export/phone_sqlradius.pm @@ -44,7 +44,6 @@ tie %options, 'Tie::IxHash', 'options' => \%options, 'notes' => <<END, Real-time export of <b>radcheck</b> table -<!--, <b>radreply</b> and <b>usergroup</b>-- tables> to any SQL database for <a href="http://www.freeradius.org/">FreeRADIUS</a> or <a href="http://radius.innercite.com/">ICRADIUS</a>. <br><br> diff --git a/FS/FS/part_svc.pm b/FS/FS/part_svc.pm index 249a2b4..cc30dbb 100644 --- a/FS/FS/part_svc.pm +++ b/FS/FS/part_svc.pm @@ -192,6 +192,8 @@ sub insert { } } + # XXX shouldn't this update fixed values? + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -714,11 +716,6 @@ sub process { my $old = qsearchs('part_svc', { 'svcpart' => $param->{'svcpart'} }) if $param->{'svcpart'}; - $param->{'svc_acct__usergroup'} = - ref($param->{'svc_acct__usergroup'}) - ? join(',', @{$param->{'svc_acct__usergroup'}} ) - : $param->{'svc_acct__usergroup'}; - #unmunge cgp_accessmodes (falze laziness-ish w/edit/process/svc_acct.cgi) $param->{'svc_acct__cgp_accessmodes'} ||= join(' ', sort @@ -737,14 +734,17 @@ sub process { } ( fields('part_svc'), map { my $svcdb = $_; my @fields = fields($svcdb); - push @fields, 'usergroup' if $svcdb eq 'svc_acct'; #kludge + push @fields, 'usergroup' if $svcdb eq 'svc_acct' + or $svcdb eq 'svc_broadband'; #kludge map { my $f = $svcdb.'__'.$_; - if ( $param->{ $f.'_flag' } =~ /^[MAH]$/ ) { + my $flag = $param->{ $f.'_flag' } || ''; #silence warnings + if ( $flag =~ /^[MAH]$/ ) { $param->{ $f } = delete( $param->{ $f.'_classnum' } ); } - if ( $param->{ $f.'_flag' } =~ /^S$/ ) { + if ( $flag =~ /^S$/ + or $_ eq 'usergroup' ) { $param->{ $f } = ref($param->{ $f }) ? join(',', @{$param->{ $f }} ) : $param->{ $f }; diff --git a/FS/FS/radius_usergroup.pm b/FS/FS/radius_usergroup.pm index 2de1423..8085fe8 100644 --- a/FS/FS/radius_usergroup.pm +++ b/FS/FS/radius_usergroup.pm @@ -96,25 +96,29 @@ and replace methods. sub check { my $self = shift; - + my $svcnum = $self->svcnum; die "radius_usergroup.groupname is deprecated" if $self->groupname; $self->ut_numbern('usergroupnum') - || $self->ut_foreign_key('svcnum','svc_acct','svcnum') + || ( $self->ut_foreign_key('svcnum','svc_acct','svcnum') + && $self->ut_foreign_key('svcnum','svc_broadband','svcnum') + && "Can't find radius_usergroup.svcnum $svcnum in svc_acct.svcnum or svc_broadband.svcnum" ) || $self->ut_foreign_key('groupnum','radius_group','groupnum') || $self->SUPER::check ; } -=item svc_acct +=item svc_x -Returns the account associated with this record (see L<FS::svc_acct>). +Returns the account associated with this record (see L<FS::svc_acct> and +L<FS::svc_broadband>). =cut sub svc_acct { my $self = shift; - qsearchs('svc_acct', { svcnum => $self->svcnum } ); + qsearchs('svc_acct', { svcnum => $self->svcnum } ) || + qsearchs('svc_broadband', { svcnum => $self->svcnum } ) } =item radius_group diff --git a/FS/FS/svc_Radius_Mixin.pm b/FS/FS/svc_Radius_Mixin.pm new file mode 100644 index 0000000..3a5ce35 --- /dev/null +++ b/FS/FS/svc_Radius_Mixin.pm @@ -0,0 +1,94 @@ +package FS::svc_Radius_Mixin; + +use strict; +use base qw(FS::m2m_Common FS::svc_Common); +use FS::Record qw(qsearch); +use FS::radius_group; +use FS::radius_usergroup; +use Carp qw(confess); + +=head1 NAME + +FS::svc_Radius_Mixin - partial base class for services with RADIUS groups + +=cut + + +sub insert { + my $self = shift; + $self->SUPER::insert(@_) + || $self->process_m2m( + 'link_table' => 'radius_usergroup', + 'target_table' => 'radius_group', + 'params' => $self->usergroup, + ); +} + +sub replace { + my $new = shift; + my $old = shift; + $old = $new->replace_old if !defined($old); + + $old->usergroup; # make sure this is cached for exports + $new->process_m2m( + 'link_table' => 'radius_usergroup', + 'target_table' => 'radius_group', + 'params' => $new->usergroup, + ) || $new->SUPER::replace($old, @_); +} + +sub delete { + my $self = shift; + $self->SUPER::delete(@_) + || $self->process_m2m( + 'link_table' => 'radius_usergroup', + 'target_table' => 'radius_group', + 'params' => [], + ); +} + +sub usergroup { + my $self = shift; + my $value = shift; + if ( defined $value ) { + if ( ref $value ) { + return $self->set('usergroup', $value); + } + else { + return $self->set('usergroup', [ split(/\s*,\s*/, $value) ]); + } + } + $self->get('usergroup') || + # if no argument is passed and usergroup is not set already, + # fetch this service's group assignments + $self->set('usergroup', + [ map { $_->groupnum } + qsearch('radius_usergroup', { svcnum => $self->svcnum }) ] + ); +} + +sub _fieldhandlers { + { + 'usergroup' => \&usergroup + } +} + +=item radius_groups METHOD + +Returns a list of RADIUS groups for this service (see L<FS::radius_usergroup>). +METHOD is the field to return, and can be any method on L<FS::radius_group>. +Useful values for METHOD include 'groupnum', 'groupname', and +'long_description'. Defaults to 'groupname' for historical reasons. + +=cut + +sub radius_groups { + my $self = shift; + my $method = shift || 'groupname'; + my $groups = join(',', @{$self->usergroup}) || return (); + my @groups = qsearch({'table' => 'radius_group', + 'extra_sql' => "where groupnum in ($groups)"}); + return map {$_->$method} @groups; +} + +1; diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index 37c55d6..12d3b8e 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -1,7 +1,10 @@ package FS::svc_acct; use strict; -use base qw( FS::svc_Domain_Mixin FS::svc_CGP_Mixin FS::svc_CGPRule_Mixin +use base qw( FS::svc_Domain_Mixin + FS::svc_CGP_Mixin + FS::svc_CGPRule_Mixin + FS::svc_Radius_Mixin FS::svc_Common ); use vars qw( $DEBUG $me $conf $skip_fuzzyfiles $dir_prefix @shells $usernamemin @@ -339,6 +342,7 @@ sub table_info { type => 'select-radius_group.html', disable_inventory => 1, disable_select => 1, + multiple => 1, }, 'seconds' => { label => 'Seconds', label_sort => 'with Time Remaining', @@ -531,22 +535,6 @@ sub table { 'svc_acct'; } sub table_dupcheck_fields { ( 'username', 'domsvc' ); } -sub _fieldhandlers { - { - #false laziness with edit/svc_acct.cgi - 'usergroup' => sub { - my( $self, $groups ) = @_; - if ( ref($groups) eq 'ARRAY' ) { - $groups; - } elsif ( length($groups) ) { - [ split(/\s*,\s*/, $groups) ]; - } else { - []; - } - }, - }; -} - sub last_login { shift->_lastlog('in', @_); } @@ -699,7 +687,7 @@ sub insert { my $dbh = dbh; my @jobnums; - my $error = $self->SUPER::insert( + my $error = $self->SUPER::insert( # usergroup is here 'jobnums' => \@jobnums, 'child_objects' => $self->child_objects, %options, @@ -709,20 +697,6 @@ sub insert { return $error; } - if ( $self->usergroup ) { - foreach my $groupnum ( @{$self->usergroup} ) { - my $radius_usergroup = new FS::radius_usergroup ( { - svcnum => $self->svcnum, - groupnum => $groupnum, - } ); - my $error = $radius_usergroup->insert; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return $error; - } - } - } - unless ( $skip_fuzzyfiles ) { $error = $self->queue_fuzzyfiles_update; if ( $error ) { @@ -935,22 +909,12 @@ sub delete { } } - my $error = $self->SUPER::delete; + my $error = $self->SUPER::delete; # usergroup here if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; } - foreach my $radius_usergroup ( - qsearch('radius_usergroup', { 'svcnum' => $self->svcnum } ) - ) { - my $error = $radius_usergroup->delete; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return $error; - } - } - $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; } @@ -1011,49 +975,7 @@ sub replace { local $FS::UID::AutoCommit = 0; my $dbh = dbh; - # redundant, but so $new->usergroup gets set - $error = $new->check; - return $error if $error; - - $old->usergroup( [ $old->radius_groups('NUMBERS') ] ); - if ( $DEBUG ) { - warn $old->email. " old groups: ". join(' ',@{$old->usergroup}). "\n"; - warn $new->email. " new groups: ". join(' ',@{$new->usergroup}). "\n"; - } - if ( $new->usergroup ) { - #(sorta) false laziness with FS::part_export::sqlradius::_export_replace - my @newgroups = @{$new->usergroup}; - foreach my $oldgroup ( @{$old->usergroup} ) { - if ( grep { $oldgroup eq $_ } @newgroups ) { - @newgroups = grep { $oldgroup ne $_ } @newgroups; - next; - } - my $radius_usergroup = qsearchs('radius_usergroup', { - svcnum => $old->svcnum, - groupnum => $oldgroup, - } ); - my $error = $radius_usergroup->delete; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "error deleting radius_usergroup $oldgroup: $error"; - } - } - - foreach my $newgroup ( @newgroups ) { - my $radius_usergroup = new FS::radius_usergroup ( { - svcnum => $new->svcnum, - groupnum => $newgroup, - } ); - my $error = $radius_usergroup->insert; - if ( $error ) { - $dbh->rollback if $oldAutoCommit; - return "error adding radius_usergroup $newgroup: $error"; - } - } - - } - - $error = $new->SUPER::replace($old, @_); + $error = $new->SUPER::replace($old, @_); # usergroup here if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error if $error; @@ -1191,15 +1113,10 @@ sub check { my($recref) = $self->hashref; - my $x = $self->setfixed( $self->_fieldhandlers ); + my $x = $self->setfixed; return $x unless ref($x); my $part_svc = $x; - if ( $part_svc->part_svc_column('usergroup')->columnflag eq "F" ) { - $self->usergroup( - [ split(',', $part_svc->part_svc_column('usergroup')->columnvalue) ] ); - } - my $error = $self->ut_numbern('svcnum') #|| $self->ut_number('domsvc') || $self->ut_foreign_key( 'domsvc', 'svc_domain', 'svcnum' ) @@ -2549,41 +2466,7 @@ sub get_cdrs { } -=item radius_groups - -Returns all RADIUS groups for this account (see L<FS::radius_usergroup>). - -=cut - -sub radius_groups { - my $self = shift; - if ( $self->usergroup ) { - confess "explicitly specified usergroup not an arrayref: ". $self->usergroup - unless ref($self->usergroup) eq 'ARRAY'; - #when provisioning records, export callback runs in svc_Common.pm before - #radius_usergroup records can be inserted... - my $groups = join(',',@{$self->usergroup}); - my @groups; - return @groups unless length($groups); - @groups = qsearch({ 'table' => 'radius_group', - 'extra_sql' => "where groupnum in ($groups)", - }); - map { $_->groupname } @groups; - } else { - my $format = shift || ''; - my @groups = qsearch({ 'table' => 'radius_usergroup', - 'addl_from' => 'left join radius_group using (groupnum)', - 'select' => 'radius_group.*', - 'hashref' => { 'svcnum' => $self->svcnum }, - }); - - # this is to preserve various legacy behaviour / avoid re-writing other code - return map { $_->groupnum } @groups if $format eq 'NUMBERS'; - return map { $_->description . " (" . $_->groupname . ")" } @groups - if $format eq 'COMBINED'; - map { $_->groupname } @groups; - } -} +# sub radius_groups has moved to svc_Radius_Mixin =item clone_suspended diff --git a/FS/FS/svc_broadband.pm b/FS/FS/svc_broadband.pm index 576684c..ad7dedc 100755 --- a/FS/FS/svc_broadband.pm +++ b/FS/FS/svc_broadband.pm @@ -9,7 +9,7 @@ use FS::addr_block; use FS::part_svc_router; use NetAddr::IP; -@ISA = qw( FS::svc_Common ); +@ISA = qw( FS::svc_Radius_Mixin FS::svc_Common ); $FS::UID::callback{'FS::svc_broadband'} = sub { $conf = new FS::Conf; @@ -115,6 +115,15 @@ sub table_info { 'longitude' => 'Longitude', 'altitude' => 'Altitude', 'vlan_profile' => 'VLAN profile', + 'usergroup' => { + label => 'RADIUS groups', + type => 'select-radius_group.html', + #select_table => 'radius_group', + #select_key => 'groupnum', + #select_label => 'groupname', + disable_inventory => 1, + multiple => 1, + }, }, }; } diff --git a/FS/bin/freeside-sqlradius-reset b/FS/bin/freeside-sqlradius-reset index a77bad6..c8da60a 100755 --- a/FS/bin/freeside-sqlradius-reset +++ b/FS/bin/freeside-sqlradius-reset @@ -19,14 +19,14 @@ adminsuidsetup $user; my @exports = (); if ( @ARGV ) { foreach my $exportnum ( @ARGV ) { - foreach my $exporttype (qw( sqlradius sqlradius_withdomain phone_sqlradius )) { + foreach my $exporttype (qw( sqlradius sqlradius_withdomain ohone_sqlradius broadband_sqlradius )) { push @exports, qsearch('part_export', { exportnum => $exportnum, exporttype => $exporttype, } ); } } } else { @exports = qsearch('part_export', { exporttype=>'sqlradius' } ); - push @exports, qsearch('part_export', { exporttype=>'sqlradius_withdomain' } ); + push @exports, qsearch('part_export', { exporttype=>'sqlradius_withdomain' } ); } unless ( $opt_n ) { @@ -34,7 +34,8 @@ unless ( $opt_n ) { my $icradius_dbh = DBI->connect( map { $export->option($_) } qw( datasrc username password ) ) or die $DBI::errstr; - for my $table (qw( radcheck radreply usergroup )) { + my $usergroup = $export->option('usergroup') || 'usergroup'; + for my $table (qw( radcheck radreply ), $usergroup) { my $sth = $icradius_dbh->prepare("DELETE FROM $table"); $sth->execute or die "Can't reset $table table: ". $sth->errstr; } |