summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
authormark <mark>2011-11-10 21:40:05 +0000
committermark <mark>2011-11-10 21:40:05 +0000
commit307a7d85568a15f5eb0d97c648507484108fcc56 (patch)
tree22e4da31ad00e299cea5dbf983234f839f17225b /FS
parentcfbfa38f73888ee2c073ad7500c1fe147cde1c81 (diff)
RADIUS groups for svc_broadband, #14695
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/Conf.pm7
-rw-r--r--FS/FS/nas.pm7
-rw-r--r--FS/FS/part_export.pm7
-rw-r--r--FS/FS/part_export/broadband_sqlradius.pm100
-rw-r--r--FS/FS/part_export/phone_sqlradius.pm1
-rw-r--r--FS/FS/part_svc.pm16
-rw-r--r--FS/FS/radius_usergroup.pm14
-rw-r--r--FS/FS/svc_Radius_Mixin.pm94
-rw-r--r--FS/FS/svc_acct.pm137
-rwxr-xr-xFS/FS/svc_broadband.pm11
-rwxr-xr-xFS/bin/freeside-sqlradius-reset7
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;
}