From c2b4bc64544ee18dae0093306b79d3d9798a02c1 Mon Sep 17 00:00:00 2001 From: Mitch Jackson Date: Tue, 28 Aug 2018 16:52:28 -0400 Subject: RT# 80669 Cancelled customers on customer churn report --- FS/FS/Report/Table.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'FS') diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm index cef7813af..7c4f97309 100644 --- a/FS/FS/Report/Table.pm +++ b/FS/FS/Report/Table.pm @@ -1115,7 +1115,7 @@ sub calculate_churn_cust { as suspended, SUM((s_active = 0 and s_suspended > 0 and e_active > 0)::int) as resumed, - SUM((s_active > 0 and e_active = 0 and e_suspended = 0)::int) + SUM((e_active = 0 and e_cancelled > s_cancelled)::int) as cancelled FROM ($cust_sql) AS x "; -- cgit v1.2.1 From fb4fda982de4c62e88edfb4302cd52c10ec95d8e Mon Sep 17 00:00:00 2001 From: Mitch Jackson Date: Sun, 26 Aug 2018 17:11:38 -0400 Subject: RT# 80869 Improve cust_payby.paydate validation --- FS/FS/Record.pm | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ FS/FS/cust_payby.pm | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) (limited to 'FS') diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 6ea936892..08d681e47 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -3207,6 +3207,60 @@ sub ut_enumn { : ''; } +=item ut_date COLUMN + +Check/untaint a column containing a date string. + +Date will be normalized to YYYY-MM-DD format + +=cut + +sub ut_date { + my ( $self, $field ) = @_; + my $value = $self->getfield( $field ); + + my @date = split /[\-\/]/, $value; + if ( scalar(@date) == 3 ) { + @date = @date[2,0,1] if $date[2] >= 1900; + + local $@; + my $ymd; + eval { + # DateTime will die given invalid date + $ymd = DateTime->new( + year => $date[0], + month => $date[1], + day => $date[2], + )->ymd('-'); + }; + + unless( $@ ) { + $self->setfield( $field, $ymd ) unless $value eq $ymd; + return ''; + } + + } + return "Illegal (date) field $field: $value"; +} + +=item ut_daten COLUMN + +Check/untaint a column containing a date string. + +Column may be null. + +Date will be normalized to YYYY-MM-DD format + +=cut + +sub ut_daten { + my ( $self, $field ) = @_; + + $self->getfield( $field ) =~ /^()$/ + ? $self->setfield( $field, '' ) + : $self->ut_date( $field ); +} + =item ut_flag COLUMN Check/untaint a column if it contains either an empty string or 'Y'. This diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm index 704741f3d..477700dc8 100644 --- a/FS/FS/cust_payby.pm +++ b/FS/FS/cust_payby.pm @@ -315,7 +315,7 @@ sub check { #encrypted #|| $self->ut_textn('payinfo') #encrypted #|| $self->ut_textn('paycvv') # || $self->ut_textn('paymask') #XXX something - #later #|| $self->ut_textn('paydate') + || $self->ut_daten('paydate') || $self->ut_numbern('paystart_month') || $self->ut_numbern('paystart_year') || $self->ut_numbern('payissue') -- cgit v1.2.1 From 05a2dd488791bb2468d40db07d17db5cfaa673fd Mon Sep 17 00:00:00 2001 From: Mitch Jackson Date: Sun, 26 Aug 2018 18:11:15 -0400 Subject: RT# 80869 freeside_upgrade fix for bad payment expiration dates --- FS/FS/cust_payby.pm | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'FS') diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm index 477700dc8..6d7ece6ca 100644 --- a/FS/FS/cust_payby.pm +++ b/FS/FS/cust_payby.pm @@ -921,9 +921,89 @@ sub _upgrade_data { local $ignore_expired_card = 1; local $ignore_invalid_card = 1; $class->upgrade_set_cardtype; + $class->_upgrade_data_paydate_edgebug; } +=item _upgrade_data_paydate_edgebug + +Correct bad data injected into payment expire date column by Edge browser bug + +The month and year values may have an extra character injected into form POST +data by Edge browser. It was possible for some bad month values to slip +past data validation. + +If the stored value was out of range, it was causing payments screen to crash. +We can detect and fix this by dropping the second digit. + +If the stored value is is 11 or 12, it's possible the user inputted a 1. In +this case, the payment method will fail to authorize, but the record will +not cause crashdumps for being out of range. + +In short, check for any expiration month > 12, and drop the extra digit + +=cut + +sub _upgrade_data_paydate_edgebug { + my $journal_label = 'cust_payby_paydate_edgebug'; + return if FS::upgrade_journal->is_done( $journal_label ); + + my $oldAutoCommit = $FS::UID::AutoCommit; + local $FS::UID::AutoCommit = 0; + + for my $row ( + FS::Record::qsearch( + cust_payby => { paydate => { op => '!=', value => '' }} + ) + ) { + next unless $row->ut_daten('paydate'); + + # paydate column stored in database has failed date validation + my $bad_paydate = $row->paydate; + + my @date = split /[\-\/]/, $bad_paydate; + @date = @date[2,0,1] if $date[2] > 1900; + + # Only autocorrecting when month > 12 - notify operator + unless ( $date[1] > 12 ) { + die sprintf( + 'Unable to correct bad paydate stored in cust_payby row '. + 'custpaybynum(%s) custnum(%s) paydate(%s)', + $row->custpaybynum, + $row->custnum, + $bad_paydate, + ); + } + + $date[1] = substr( $date[1], 0, 1 ); + $row->paydate( join('-', @date )); + + if ( my $error = $row->replace ) { + die sprintf( + 'Failed to autocorrect bad paydate stored in cust_payby row '. + 'custpaybynum(%s) custnum(%s) paydate(%s) - error: %s', + $row->custpaybynum, + $row->custnum, + $bad_paydate, + $error + ); + } + + warn sprintf( + 'Autocorrected bad paydate stored in cust_payby row '. + "custpaybynum(%s) custnum(%s) old-paydate(%s) new-paydate(%s)\n", + $row->custpaybynum, + $row->custnum, + $bad_paydate, + $row->paydate, + ); + + } + + FS::upgrade_journal->set_done( $journal_label ); + dbh->commit unless $oldAutoCommit; +} + =head1 BUGS =head1 SEE ALSO -- cgit v1.2.1 From fe0e5a810960200b5352128139e6b040ea08eada Mon Sep 17 00:00:00 2001 From: Mitch Jackson Date: Tue, 28 Aug 2018 14:05:17 -0400 Subject: RT# 80869 Improve cust_payby.paydate validation --- FS/FS/cust_payby.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'FS') diff --git a/FS/FS/cust_payby.pm b/FS/FS/cust_payby.pm index 6d7ece6ca..c497059fa 100644 --- a/FS/FS/cust_payby.pm +++ b/FS/FS/cust_payby.pm @@ -315,7 +315,6 @@ sub check { #encrypted #|| $self->ut_textn('payinfo') #encrypted #|| $self->ut_textn('paycvv') # || $self->ut_textn('paymask') #XXX something - || $self->ut_daten('paydate') || $self->ut_numbern('paystart_month') || $self->ut_numbern('paystart_year') || $self->ut_numbern('payissue') @@ -546,6 +545,9 @@ sub check { return $error if $error; } + $error = $self->ut_daten('paydate'); + return $error if $error; + $self->SUPER::check; } -- cgit v1.2.1 From 150aefab60e1b4c4b66533f644cf67893e4d0659 Mon Sep 17 00:00:00 2001 From: Christopher Burger Date: Fri, 7 Sep 2018 10:55:19 -0400 Subject: RT# 75817 - fixed saving of password for new contacts, and password validation on dynamicly created rows Conflicts: httemplate/edit/elements/edit.html --- FS/FS/contact.pm | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'FS') diff --git a/FS/FS/contact.pm b/FS/FS/contact.pm index 02c6d0659..fc06ca82b 100644 --- a/FS/FS/contact.pm +++ b/FS/FS/contact.pm @@ -199,8 +199,6 @@ sub insert { } - $error ||= $self->insert_password_history; - if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -302,6 +300,15 @@ sub insert { } } + if ( $self->get('password') ) { + my $error = $self->is_password_allowed($self->get('password')) + || $self->change_password($self->get('password')); + if ( $error ) { + $dbh->rollback if $oldAutoCommit; + return $error; + } + } + $dbh->commit or die $dbh->errstr if $oldAutoCommit; ''; @@ -817,7 +824,7 @@ sub authenticate_password { $hash eq $check_hash; - } else { + } else { return 0 if $self->_password eq ''; -- cgit v1.2.1 From e55e22e00eba74f2713b52fccbbb380f454531f4 Mon Sep 17 00:00:00 2001 From: Christopher Burger Date: Mon, 10 Sep 2018 14:25:44 -0400 Subject: RT# 81131 - updated Saisei export to create a virtual ap for multiple ips per customer --- FS/FS/part_export/saisei.pm | 60 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) (limited to 'FS') diff --git a/FS/FS/part_export/saisei.pm b/FS/FS/part_export/saisei.pm index c79f79dac..3af3c9d9e 100644 --- a/FS/FS/part_export/saisei.pm +++ b/FS/FS/part_export/saisei.pm @@ -200,12 +200,28 @@ sub _export_insert { my $accesspoint = process_sector($self, $sector_opt); return $self->api_error if $self->{'__saisei_error'}; +## get custnum and pkgpart from cust_pkg for virtual access point + my $cust_pkg = FS::Record::qsearchs({ + 'table' => 'cust_pkg', + 'hashref' => { 'pkgnum' => $svc_broadband->{Hash}->{pkgnum}, }, + }); + my $virtual_ap_name = $cust_pkg->{Hash}->{custnum}.'_'.$cust_pkg->{Hash}->{pkgpart}.'_'.$svc_broadband->{Hash}->{speed_down}.'_'.$svc_broadband->{Hash}->{speed_up}; + + my $virtual_ap_opt = { + 'virtual_name' => $virtual_ap_name, + 'sector_name' => $sector_name, + 'virtual_uprate_limit' => $svc_broadband->{Hash}->{speed_up}, + 'virtual_downrate_limit' => $svc_broadband->{Hash}->{speed_down}, + }; + my $virtual_ap = process_virtual_ap($self, $virtual_ap_opt); + return $self->api_error if $self->{'__saisei_error'}; + ## tie host to user add sector name as access point. $self->api_add_host_to_user( $user->{collection}->[0]->{name}, $rateplan->{collection}->[0]->{name}, $svc_broadband->{Hash}->{ip_addr}, - $accesspoint->{collection}->[0]->{name}, + $virtual_ap->{collection}->[0]->{name}, ) unless $self->{'__saisei_error'}; } @@ -215,8 +231,8 @@ sub _export_insert { sub _export_replace { my ($self, $svc_broadband) = @_; - $self->_export_insert($svc_broadband); - return ''; + my $error = $self->_export_insert($svc_broadband); + return $error; } sub _export_delete { @@ -804,6 +820,44 @@ sub process_sector { return $accesspoint; } +sub process_virtual_ap { + my ($self, $opt) = @_; + + my $existing_virtual_ap; + my $virtual_name = $opt->{virtual_name}; + + #check if sector has been set up as an access point. + $existing_virtual_ap = $self->api_get_accesspoint($virtual_name); + + # modify the existing virtual accesspoint if changing it. this should never happen + $self->api_modify_existing_accesspoint ( + $virtual_name, + $opt->{sector_name}, + $opt->{virtual_uprate_limit}, + $opt->{virtual_downrate_limit}, + ) if $existing_virtual_ap && $opt->{modify_existing}; + + #if virtual ap does not exist as an access point create it. + $self->api_create_accesspoint( + $virtual_name, + $opt->{virtual_uprate_limit}, + $opt->{virtual_downrate_limit}, + ) unless $existing_virtual_ap; + +my $update_sector; +if ($existing_virtual_ap && ($existing_virtual_ap->{collection}->[0]->{uplink}->{link}->{name} ne $opt->{sector_name})) { + $update_sector = 1; +} + + # Attach newly created virtual ap to tower sector ap or if sector has changed. + $self->api_modify_accesspoint($virtual_name, $opt->{sector_name}) unless ($self->{'__saisei_error'} || ($existing_virtual_ap && !$update_sector)); + + # set access point to existing one or newly created one. + my $accesspoint = $existing_virtual_ap ? $existing_virtual_ap : $self->api_get_accesspoint($virtual_name); + + return $accesspoint; +} + sub export_provisioned_services { my $job = shift; my $param = shift; -- cgit v1.2.1 From 76a893c2187f59e7fdb9bc8282b780efd9dd8504 Mon Sep 17 00:00:00 2001 From: Christopher Burger Date: Tue, 16 Jan 2018 12:14:54 -0500 Subject: RT# 39340 - Created minimal selfservice that only allows payments to be made, authorization is based on ip and mac address. This is not done yet need to write routine to get mac address from radius server based on ip address. --- FS/FS/ClientAPI/MyAccount.pm | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'FS') diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index dbecb9b67..a30dde568 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -237,6 +237,13 @@ sub login { $svc_x = $svc_phone; + } elsif ( $p->{'domain'} eq 'ip_mac' ) { + + my $svc_broadband = qsearchs( 'svc_broadband', { 'mac_addr' => $p->{'username'} } ); + return { error => 'IP address not found' } + unless $svc_broadband; + $svc_x = $svc_broadband; + } elsif ( $p->{email} && (my $contact = FS::contact->by_selfservice_email($p->{email})) ) -- cgit v1.2.1 From bc552f2fba7fd376a2f036a7dc02b58a48fa31b0 Mon Sep 17 00:00:00 2001 From: Christopher Burger Date: Tue, 11 Sep 2018 11:27:42 -0400 Subject: RT# 39340 - configured the minimal selfservice to get mac address from radius account table --- FS/FS/ClientAPI/MyAccount.pm | 30 ++++++++++++++++++++++++++++-- FS/FS/ClientAPI_XMLRPC.pm | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) (limited to 'FS') diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index a30dde568..fe77243e5 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -184,6 +184,29 @@ sub skin_info { } +sub get_mac_address { + my $p = shift; + +## access radius exports acct tables to get mac + my @part_export = (); + @part_export = ( + qsearch( 'part_export', { 'exporttype' => 'sqlradius' } ), + qsearch( 'part_export', { 'exporttype' => 'sqlradius_withdomain' } ), + qsearch( 'part_export', { 'exporttype' => 'broadband_sqlradius' } ), + ); + + my @sessions; + foreach my $part_export (@part_export) { + push @sessions, ( @{ $part_export->usage_sessions( { + 'ip' => $p->{'ip'}, + } ) } ); + } + + my $mac = $sessions[0]->{'callingstationid'}; + + return { 'mac_address' => $mac, }; +} + sub login_info { my $p = shift; @@ -239,8 +262,11 @@ sub login { } elsif ( $p->{'domain'} eq 'ip_mac' ) { - my $svc_broadband = qsearchs( 'svc_broadband', { 'mac_addr' => $p->{'username'} } ); - return { error => 'IP address not found' } + my $mac_address = $p->{'username'}; + $mac_address =~ s/\://g; + + my $svc_broadband = qsearchs( 'svc_broadband', { 'mac_addr' => $mac_address } ); + return { error => 'MAC address not found '.$p->{'username'} } unless $svc_broadband; $svc_x = $svc_broadband; diff --git a/FS/FS/ClientAPI_XMLRPC.pm b/FS/FS/ClientAPI_XMLRPC.pm index dcf34fdaa..db0537c02 100644 --- a/FS/FS/ClientAPI_XMLRPC.pm +++ b/FS/FS/ClientAPI_XMLRPC.pm @@ -227,6 +227,7 @@ sub ss2clientapi { 'quotation_add_pkg' => 'MyAccount/quotation/quotation_add_pkg', 'quotation_remove_pkg' => 'MyAccount/quotation/quotation_remove_pkg', 'quotation_order' => 'MyAccount/quotation/quotation_order', + 'get_mac_address' => 'MyAccount/get_mac_address', 'freesideinc_service' => 'Freeside/freesideinc_service', }; -- cgit v1.2.1