X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2Fsvc_acct.pm;h=af152a82ed8554c5fc4a32bbb3652af4632f4d27;hb=6f471946b3ae36061cb595ba0657a5b43e7bfd5d;hp=6b4838401e4dbc2255c8abeef36dc10aab3f2226;hpb=6636a4ed5401d2915efb9aabe5345c7ad70f579e;p=freeside.git diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index 6b4838401..af152a82e 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -7,7 +7,11 @@ use base qw( FS::svc_Domain_Mixin FS::svc_Radius_Mixin FS::svc_Tower_Mixin FS::svc_IP_Mixin - FS::svc_Common ); + FS::Password_Mixin + FS::svc_Common + ); + +use strict; use vars qw( $DEBUG $me $conf $skip_fuzzyfiles $dir_prefix @shells $usernamemin $usernamemax $passwordmin $passwordmax @@ -40,6 +44,7 @@ use FS::Record qw( qsearch qsearchs fields dbh dbdef ); use FS::Msgcat qw(gettext); use FS::UI::bytecount; use FS::UI::Web; +use FS::PagedSearch qw( psearch ); # XXX in v4, replace with FS::Cursor use FS::part_pkg; use FS::part_svc; use FS::svc_acct_pop; @@ -260,6 +265,7 @@ sub table_info { 'display_weight' => 10, 'cancel_weight' => 50, 'ip_field' => 'slipip', + 'manual_require' => 1, 'fields' => { 'dir' => 'Home directory', 'uid' => { @@ -283,6 +289,7 @@ sub table_info { disable_default => 1, disable_fixed => 1, disable_select => 1, + required => 1, }, 'password_selfchange' => { label => 'Password modification', type => 'checkbox', @@ -294,27 +301,25 @@ sub table_info { label => 'Quota', #Mail storage limit type => 'text', disable_inventory => 1, - disable_select => 1, }, 'file_quota'=> { label => 'File storage limit', type => 'text', disable_inventory => 1, - disable_select => 1, }, 'file_maxnum'=> { label => 'Number of files limit', type => 'text', disable_inventory => 1, - disable_select => 1, }, 'file_maxsize'=> { label => 'File size limit', type => 'text', disable_inventory => 1, - disable_select => 1, }, - '_password' => 'Password', + '_password' => { label => 'Password', + #required => 1 + }, 'gid' => { label => 'GID', def_info => 'when blank, defaults to UID', @@ -337,6 +342,7 @@ sub table_info { select_key => 'svcnum', select_label => 'domain', disable_inventory => 1, + required => 1, }, 'pbxsvc' => { label => 'PBX', type => 'select-svc_pbx.html', @@ -699,6 +705,9 @@ sub insert { 'child_objects' => $self->child_objects, %options, ); + + $error ||= $self->insert_password_history; + if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error; @@ -983,6 +992,12 @@ sub replace { my $dbh = dbh; $error = $new->SUPER::replace($old, @_); # usergroup here + + # don't need to record this unless the password was changed + if ( $old->_password ne $new->_password ) { + $error ||= $new->insert_password_history; + } + if ( $error ) { $dbh->rollback if $oldAutoCommit; return $error if $error; @@ -1895,22 +1910,22 @@ sub email { $self->username. '@'. $self->domain(@_); } -# snarfs are unused at this point? -# -# =item acct_snarf -# -# Returns an array of FS::acct_snarf records associated with the account. -# -# =cut -# -# sub acct_snarf { -# my $self = shift; -# qsearch({ -# 'table' => 'acct_snarf', -# 'hashref' => { 'svcnum' => $self->svcnum }, -# #'order_by' => 'ORDER BY priority ASC', -# }); -# } + +=item acct_snarf + +Returns an array of FS::acct_snarf records associated with the account. + +=cut + +# unused as originally intended, but now by Communigate Pro "RPOP" +sub acct_snarf { + my $self = shift; + qsearch({ + 'table' => 'acct_snarf', + 'hashref' => { 'svcnum' => $self->svcnum }, + #'order_by' => 'ORDER BY priority ASC', + }); +} =item cgp_rpop_hashref @@ -2369,130 +2384,105 @@ sub seconds_since { $self->cust_svc->seconds_since(@_); } -=item seconds_since_sqlradacct TIMESTAMP_START TIMESTAMP_END - -Returns the numbers of seconds this account has been online between -TIMESTAMP_START (inclusive) and TIMESTAMP_END (exclusive), according to an -external SQL radacct table, specified via sqlradius export. Sessions which -started in the specified range but are still open are counted from session -start to the end of the range (unless they are over 1 day old, in which case -they are presumed missing their stop record and not counted). Also, sessions -which end in the range but started earlier are counted from the start of the -range to session end. Finally, sessions which start before the range but end -after are counted for the entire range. +=item last_login_text -TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see -L. Also see L and L for conversion -functions. +Returns text describing the time of last login. =cut -#note: POD here, implementation in FS::cust_svc -sub seconds_since_sqlradacct { +sub last_login_text { my $self = shift; - $self->cust_svc->seconds_since_sqlradacct(@_); + $self->last_login ? ctime($self->last_login) : 'unknown'; } -=item attribute_since_sqlradacct TIMESTAMP_START TIMESTAMP_END ATTRIBUTE +=item psearch_cdrs OPTIONS -Returns the sum of the given attribute for all accounts (see L) -in this package for sessions ending between TIMESTAMP_START (inclusive) and -TIMESTAMP_END (exclusive). - -TIMESTAMP_START and TIMESTAMP_END are specified as UNIX timestamps; see -L. Also see L and L for conversion -functions. +Returns a paged search (L) for Call Detail Records +associated with this service. For svc_acct, "associated with" means that +either the "src" or the "charged_party" field of the CDR matches the +"username" field of the service. =cut -#note: POD here, implementation in FS::cust_svc -sub attribute_since_sqlradacct { - my $self = shift; - $self->cust_svc->attribute_since_sqlradacct(@_); -} +sub psearch_cdrs { + my($self, %options) = @_; + my @fields; + my %hash; + my @where; -=item get_session_history TIMESTAMP_START TIMESTAMP_END + my $did = dbh->quote($self->username); -Returns an array of hash references of this customers login history for the -given time range. (document this better) - -=cut + my $prefix = $options{'default_prefix'} || ''; #convergent.au '+61' + my $prefixdid = dbh->quote($prefix . $self->username); -sub get_session_history { - my $self = shift; - $self->cust_svc->get_session_history(@_); -} + my $for_update = $options{'for_update'} ? 'FOR UPDATE' : ''; -=item last_login_text + if ( $options{inbound} ) { + # these will be selected under their DIDs + push @where, "FALSE"; + } -Returns text describing the time of last login. + my @orwhere; + if (!$options{'disable_charged_party'}) { + push @orwhere, + "charged_party = $did", + "charged_party = $prefixdid"; + } + if (!$options{'disable_src'}) { + push @orwhere, + "src = $did AND charged_party IS NULL", + "src = $prefixdid AND charged_party IS NULL"; + } + push @where, '(' . join(' OR ', @orwhere) . ')'; -=cut + # $options{'status'} = '' is meaningful; for the rest of them it's not + if ( exists $options{'status'} ) { + $hash{'freesidestatus'} = $options{'status'}; + } + if ( $options{'cdrtypenum'} ) { + $hash{'cdrtypenum'} = $options{'cdrtypenum'}; + } + if ( $options{'calltypenum'} ) { + $hash{'calltypenum'} = $options{'calltypenum'}; + } + if ( $options{'begin'} ) { + push @where, 'startdate >= '. $options{'begin'}; + } + if ( $options{'end'} ) { + push @where, 'startdate < '. $options{'end'}; + } + if ( $options{'nonzero'} ) { + push @where, 'duration > 0'; + } -sub last_login_text { - my $self = shift; - $self->last_login ? ctime($self->last_login) : 'unknown'; + my $extra_sql = join(' AND ', @where); + if ($extra_sql) { + if (keys %hash) { + $extra_sql = " AND ".$extra_sql; + } else { + $extra_sql = " WHERE ".$extra_sql; + } + } + return psearch({ + 'select' => '*', + 'table' => 'cdr', + 'hashref' => \%hash, + 'extra_sql' => $extra_sql, + 'order_by' => "ORDER BY startdate $for_update", + }); } -=item get_cdrs TIMESTAMP_START TIMESTAMP_END [ 'OPTION' => 'VALUE ... ] +=item get_cdrs (DEPRECATED) + +Like psearch_cdrs, but returns all the L objects at once, in a +single list. Arguments are the same as for psearch_cdrs. =cut sub get_cdrs { - my($self, $start, $end, %opt ) = @_; - - my $did = $self->username; #yup - - my $prefix = $opt{'default_prefix'}; #convergent.au '+61' - - my $for_update = $opt{'for_update'} ? 'FOR UPDATE' : ''; - - #SELECT $for_update * FROM cdr - # WHERE calldate >= $start #need a conversion - # AND calldate < $end #ditto - # AND ( charged_party = "$did" - # OR charged_party = "$prefix$did" #if length($prefix); - # OR ( ( charged_party IS NULL OR charged_party = '' ) - # AND - # ( src = "$did" OR src = "$prefix$did" ) # if length($prefix) - # ) - # ) - # AND ( freesidestatus IS NULL OR freesidestatus = '' ) - - my $charged_or_src; - if ( length($prefix) ) { - $charged_or_src = - " AND ( charged_party = '$did' - OR charged_party = '$prefix$did' - OR ( ( charged_party IS NULL OR charged_party = '' ) - AND - ( src = '$did' OR src = '$prefix$did' ) - ) - ) - "; - } else { - $charged_or_src = - " AND ( charged_party = '$did' - OR ( ( charged_party IS NULL OR charged_party = '' ) - AND - src = '$did' - ) - ) - "; - - } - - qsearch( - 'select' => "$for_update *", - 'table' => 'cdr', - 'hashref' => { - #( freesidestatus IS NULL OR freesidestatus = '' ) - 'freesidestatus' => '', - }, - 'extra_sql' => $charged_or_src, - - ); - + my $self = shift; + my $psearch = $self->psearch_cdrs(@_); + qsearch ( $psearch->{query} ) } # sub radius_groups has moved to svc_Radius_Mixin @@ -2785,6 +2775,25 @@ sub virtual_maildir { $self->domain. '/maildirs/'. $self->username. '/'; } +=item password_svc_check + +Override, for L. Not really intended for other use. + +=cut + +sub password_svc_check { + my ($self, $password) = @_; + foreach my $field ( qw(username finger) ) { + foreach my $word (split(/\W+/,$self->get($field))) { + next unless length($word) > 2; + if ($password =~ /$word/i) { + return qq(Password contains account information '$word'); + } + } + } + return ''; +} + =back =head1 CLASS METHODS