diff options
| author | mark <mark> | 2011-11-10 21:40:25 +0000 | 
|---|---|---|
| committer | mark <mark> | 2011-11-10 21:40:25 +0000 | 
| commit | 7aef2e455fe8a4722036f231ee8609ac63b2a9c9 (patch) | |
| tree | e54f586e587f6b58ae2be2e342d779974212a062 /FS | |
| parent | bf8ba5bcc6d128c3ab8e498723257ee54e1db0a7 (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 4104fda9c..8d26e91aa 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 4564a6342..af5a23a24 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 f84f2a096..4b60953cf 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 000000000..ae0876ddf --- /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 0816efab6..6b14bed3c 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 4a1b30877..1bb66ed97 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;    ''; @@ -736,11 +738,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 @@ -759,14 +756,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 2de142397..8085fe805 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 000000000..3a5ce358d --- /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 37c55d67c..12d3b8ecf 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 576684c56..ad7dedcdd 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 a77bad64f..c8da60a4e 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;      }  | 
