Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • smradius/smradius
  • centiva-shail/smradius
  • nkukard/smradius
3 results
Show changes
Showing
with 4800 additions and 1025 deletions
# FUP support
# Copyright (C) 2007-2019, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::features::mod_feature_fup;
use strict;
use warnings;
# Modules we need
use smradius::attributes;
use smradius::constants;
use smradius::logging;
use smradius::util;
use AWITPT::Util;
use List::Util qw( min );
use MIME::Lite;
use POSIX qw( floor );
# Set our version
our $VERSION = "0.0.1";
# Load exporter
use base qw(Exporter);
our @EXPORT = qw(
);
our @EXPORT_OK = qw(
);
# Plugin info
our $pluginInfo = {
Name => "User FUP Feature",
Init => \&init,
# Authentication hook
'Feature_Post-Authentication_hook' => \&post_auth_hook,
# Accounting hook
'Feature_Post-Accounting_hook' => \&post_acct_hook,
};
# Some constants
my $FUP_PERIOD_ATTRIBUTE = 'SMRadius-FUP-Period';
my $FUP_TRAFFIC_THRESHOLD_ATTRIBUTE = 'SMRadius-FUP-Traffic-Threshold';
my $config;
## @internal
# Initialize module
sub init
{
my $server = shift;
my $scfg = $server->{'inifile'};
# Defaults
$config->{'enable_mikrotik'} = 0;
# Setup SQL queries
if (defined($scfg->{'mod_feature_fup'})) {
# Check if option exists
if (defined($scfg->{'mod_feature_fup'}{'enable_mikrotik'})) {
# Pull in config
if (defined(my $val = isBoolean($scfg->{'mod_feature_fup'}{'enable_mikrotik'}))) {
if ($val) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_FUP] Mikrotik-specific vendor return attributes ENABLED");
$config->{'enable_mikrotik'} = $val;
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_FUP] Value for 'enable_mikrotik' is invalid");
}
}
}
return;
}
## @post_auth_hook($server,$user,$packet)
# Post authentication hook
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub post_auth_hook
{
my ($server,$user,$packet) = @_;
# Skip MAC authentication
return MOD_RES_SKIP if ($user->{'_UserDB'}->{'Name'} eq "SQL User Database (MAC authentication)");
$server->log(LOG_DEBUG,"[MOD_FEATURE_FUP] POST AUTH HOOK");
#
# Get threshold from attributes
#
my $fupPeriod = _getAttributeKeyNumeric($server,$user,$FUP_PERIOD_ATTRIBUTE);
my $trafficThreshold = _getAttributeKeyNumeric($server,$user,$FUP_TRAFFIC_THRESHOLD_ATTRIBUTE);
# If we have no FUP period, skip
if (!defined($fupPeriod)) {
return MOD_RES_SKIP;
};
# If we have no traffic threshold, display an info message and skip
if (!defined($trafficThreshold)) {
$server->log(LOG_INFO,"[MOD_FEATURE_FUP] User has a '$FUP_PERIOD_ATTRIBUTE' defined, but NOT a ".
"'$FUP_TRAFFIC_THRESHOLD_ATTRIBUTE' attribute, aborting FUP checks.");
return MOD_RES_SKIP;
};
#
# Get current traffic and uptime usage
#
my $accountingUsage = _getAccountingUsage($server,$user,$packet,$fupPeriod);
if (!defined($accountingUsage)) {
return MOD_RES_SKIP;
}
#
# Display our FUP info
#
_logUsage($server,$fupPeriod,$accountingUsage->{'TotalDataUsage'},$trafficThreshold);
#
# Check if the user has exceeded the FUP
#
my $fupExceeded = ($accountingUsage->{'TotalDataUsage'} > $trafficThreshold) ? 1 : 0;
#
# Add conditional variables
#
addAttributeConditionalVariable($user,"SMRadius_FUP",$fupExceeded);
return MOD_RES_ACK;
}
## @post_acct_hook($server,$user,$packet)
# Post authentication hook
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub post_acct_hook
{
my ($server,$user,$packet) = @_;
# We cannot cap a user if we don't have a UserDB module can we? no userdb, no cap?
return MOD_RES_SKIP if (!defined($user->{'_UserDB'}->{'Name'}));
# Skip MAC authentication
return MOD_RES_SKIP if ($user->{'_UserDB'}->{'Name'} eq "SQL User Database (MAC authentication)");
# User is either connecting 'START' or disconnecting 'STOP'
return MOD_RES_SKIP if ($packet->rawattr('Acct-Status-Type') ne "1" && $packet->rawattr('Acct-Status-Type') ne "3");
$server->log(LOG_DEBUG,"[MOD_FEATURE_FUP] POST ACCT HOOK");
#
# Get threshold from attributes
#
my $fupPeriod = _getAttributeKeyNumeric($server,$user,$FUP_PERIOD_ATTRIBUTE);
my $trafficThreshold = _getAttributeKeyNumeric($server,$user,$FUP_TRAFFIC_THRESHOLD_ATTRIBUTE);
# If we have no FUP period, skip
if (!defined($fupPeriod)) {
return MOD_RES_SKIP;
};
# If we have no traffic threshold, display an info message and skip
if (!defined($trafficThreshold)) {
$server->log(LOG_INFO,"[MOD_FEATURE_FUP] User has a '$FUP_PERIOD_ATTRIBUTE' defined, but NOT a ".
"'$FUP_TRAFFIC_THRESHOLD_ATTRIBUTE' attribute, aborting FUP checks.");
return MOD_RES_SKIP;
};
#
# Get current traffic and uptime usage
#
my $accountingUsage = _getAccountingUsage($server,$user,$packet,$fupPeriod);
if (!defined($accountingUsage)) {
return MOD_RES_SKIP;
}
#
# Display our FUP info
#
_logUsage($server,$fupPeriod,$accountingUsage->{'TotalDataUsage'},$trafficThreshold);
#
# Check if the user has exceeded the FUP
#
my $fupExceeded = ($accountingUsage->{'TotalDataUsage'} > $trafficThreshold) ? 1 : 0;
#
# Add conditional variables
#
addAttributeConditionalVariable($user,"SMRadius_FUP",$fupExceeded);
return MOD_RES_ACK;
}
## @internal
# Code snippet to grab the current uptime limit by processing the user attributes
sub _getAttributeKeyNumeric
{
my ($server,$user,$attributeKey) = @_;
# Short circuit return if we don't have the uptime key set
return if (!defined($user->{'Attributes'}->{$attributeKey}));
# Short circuit if we do not have a valid attribute operator: ':='
if (!defined($user->{'Attributes'}->{$attributeKey}->{':='})) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_FUP] No valid operators for attribute '".
$user->{'Attributes'}->{$attributeKey}."'");
return;
}
$server->log(LOG_DEBUG,"[MOD_FEATURE_FUP] Attribute '".$attributeKey."' is defined");
# Check for valid attribute value
if (!defined($user->{'Attributes'}->{$attributeKey}->{':='}->{'Value'}) ||
$user->{'Attributes'}->{$attributeKey}->{':='}->{'Value'} !~ /^\d+$/) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_FUP] Attribute '".$user->{'Attributes'}->{$attributeKey}->{':='}->{'Value'}.
"' is NOT a numeric value");
return;
}
return $user->{'Attributes'}->{$attributeKey}->{':='}->{'Value'};
}
## @internal
# Code snippet to grab the accounting usage of a user for a specific period
sub _getAccountingUsage
{
my ($server,$user,$packet,$period) = @_;
foreach my $module (@{$server->{'module_list'}}) {
# Do we have the correct plugin?
if (defined($module->{'Accounting_getUsage'})) {
$server->log(LOG_INFO,"[MOD_FEATURE_FUP] Found plugin: '".$module->{'Name'}."'");
# Fetch users session uptime & bandwidth used for a specific period
if (my $res = $module->{'Accounting_getUsage'}($server,$user,$packet,$period)) {
return $res;
}
$server->log(LOG_ERR,"[MOD_FEATURE_FUP] No usage data found for user '".$user->{'Username'}."'");
}
}
return;
}
## @internal
# Code snippet to log our FUP information
sub _logUsage
{
my ($server,$period,$total,$threshold) = @_;
$server->log(LOG_INFO,"[MOD_FEATURE_FUP] FUP information [period: %s days, total: %s, threshold: %s]",
$period,$total,$threshold);
return;
}
## @internal
# Function snippet to return a attribute
sub _getAttribute
{
my ($server,$user,$attributeName) = @_;
# Check the attribute exists
return if (!defined($user->{'Attributes'}->{$attributeName}));
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] User attribute '".$attributeName."' is defined");
# Check the required operator is present in this case :=
if (!defined($user->{'Attributes'}->{$attributeName}->{':='})) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] User attribute '".$attributeName."' has no ':=' operator");
return;
}
# Check the operator value is defined...
if (!defined($user->{'Attributes'}->{$attributeName}->{':='}->{'Value'})) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] User attribute '".$attributeName."' has no value");
return;
}
return $user->{'Attributes'}->{$attributeName}->{':='}->{'Value'};
}
1;
# vim: ts=4
# Support for updating of user stats
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::features::mod_feature_update_user_stats_sql;
use strict;
use warnings;
# Modules we need
use smradius::constants;
use AWITPT::DB::DBLayer;
use smradius::logging;
use smradius::util;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
);
@EXPORT_OK = qw(
);
# Plugin info
our $pluginInfo = {
Name => "Update User Stats",
Init => \&init,
# Accounting hook
'Feature_Post-Accounting_hook' => \&updateUserStats
};
# Module config
my $config;
## @internal
# Initialize module
sub init
{
my $server = shift;
my $scfg = $server->{'inifile'};
# Enable support for database
$server->log(LOG_NOTICE,"[MOD_FEATURE_UPDATE_USER_STATS_SQL] Enabling database support");
if (!$server->{'smradius'}->{'database'}->{'enabled'}) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_UPDATE_USER_STATS_SQL] Enabling database support.");
$server->{'smradius'}->{'database'}->{'enabled'} = 1;
}
# Default configs...
$config->{'update_user_stats_query'} = '
UPDATE
@TP@users
SET
PeriodKey = %{query.PeriodKey},
TotalTraffic = %{query.TotalTraffic},
TotalUptime = %{query.TotalUptime},
NASIdentifier = %{request.NAS-Identifier},
LastAcctUpdate = now()
WHERE
Username = %{user.Username}
';
# Setup SQL queries
if (defined($scfg->{'mod_feature_update_user_stats_sql'})) {
# Pull in queries
if (defined($scfg->{'mod_feature_update_user_stats_sql'}->{'update_user_stats_query'}) &&
$scfg->{'mod_feature_update_user_stats_sql'}->{'update_user_stats_query'} ne "") {
if (ref($scfg->{'mod_feature_update_user_stats_sql'}->{'update_user_stats_query'}) eq "ARRAY") {
$config->{'update_user_stats_query'} = join(' ',
@{$scfg->{'mod_feature_update_user_stats_sql'}->{'update_user_stats_query'}});
} else {
$config->{'update_user_stats_query'} = $scfg->{'mod_feature_update_user_stats_sql'}->{'update_user_stats_query'};
}
}
}
}
## @updateUserStats($server,$user,$packet)
# Post authentication hook
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub updateUserStats
{
my ($server,$user,$packet) = @_;
# Skip MAC authentication
return MOD_RES_SKIP if (defined($user->{'_UserDB'}->{'Name'}) &&
$user->{'_UserDB'}->{'Name'} eq "SQL User Database (MAC authentication)");
$server->log(LOG_DEBUG,"[MOD_FEATURE_UPDATE_USER_STATS_SQL] UPDATE USER STATS HOOK");
# Build template
my $template;
foreach my $attr ($packet->attributes) {
$template->{'request'}->{$attr} = $packet->rawattr($attr)
}
# Add user details
$template->{'user'}->{'ID'} = $user->{'ID'};
$template->{'user'}->{'Username'} = $user->{'Username'};
# Current PeriodKey
my $now = DateTime->now->set_time_zone($server->{'smradius'}->{'event_timezone'});
$template->{'query'}->{'PeriodKey'} = $now->strftime("%Y-%m");
# Loop with plugins to find anything supporting getting of usage
my $accountingUsage;
foreach my $module (@{$server->{'module_list'}}) {
# Do we have the correct plugin?
if ($module->{'Accounting_getUsage'}) {
$server->log(LOG_INFO,"[MOD_FEATURE_UPDATE_USER_STATS_SQL] Found plugin: '".$module->{'Name'}."'");
# Fetch users session uptime & bandwidth used
my $res = $module->{'Accounting_getUsage'}($server,$user,$packet);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_FEATURE_UPDATE_USER_STATS_SQL] No usage data found for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
}
$accountingUsage = $res;
}
}
# Add to our template hash
$template->{'query'}->{'TotalTraffic'} = $accountingUsage->{'TotalDataUsage'};
$template->{'query'}->{'TotalUptime'} = $accountingUsage->{'TotalSessionTime'};
# Replace template entries
my (@dbDoParams) = templateReplace($config->{'update_user_stats_query'},$template);
# Perform query
my $sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_FEATURE_UPDATE_USER_STATS_SQL] Database query failed: ".AWITPT::DB::DBLayer::error());
return;
}
return MOD_RES_ACK;
}
1;
# vim: ts=4
# Support for updating user data
# Copyright (C) 2007-2011, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::features::mod_feature_user_stats;
use strict;
use warnings;
# Modules we need
use smradius::constants;
use AWITPT::DB::DBLayer;
use smradius::logging;
use smradius::util;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
);
@EXPORT_OK = qw(
);
# Plugin info
our $pluginInfo = {
Name => "User Stats",
Init => \&init,
# Accounting hook
'Feature_Post-Accounting_hook' => \&updateUserStats,
# 'Feature_Post-Accounting_hook' => \&getUserStats
};
## @internal
# Initialize module
sub init
{
my $server = shift;
}
## @updateUserStats($server,$user,$packet)
# Post accounting hook
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub updateUserStats
{
my ($server,$user,$packet) = @_;
# We cannot cap a user if we don't have a UserDB module can we? no userdb, no means to store info?
return MOD_RES_SKIP if (!defined($user->{'_UserDB'}->{'Name'}));
# Skip MAC authentication
return MOD_RES_SKIP if ($user->{'_UserDB'}->{'Name'} eq "SQL User Database (MAC authentication)");
$server->log(LOG_DEBUG,"[MOD_FEATURE_UPDATE_USER_STATS] UPDATE USER STATS HOOK");
# Variables we are going to set
my $currentUsage;
# Loop with plugins to find anything supporting getting of usage
foreach my $module (@{$server->{'module_list'}}) {
# Do we have the correct plugin?
if ($module->{'Accounting_getUsage'}) {
$server->log(LOG_INFO,"[MOD_USERS_DATA] Found plugin: '".$module->{'Name'}."'");
# Fetch users session uptime & bandwidth used
my $res = $module->{'Accounting_getUsage'}($server,$user,$packet);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_USERS_DATA] No usage data found for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
}
$currentUsage = $res;
}
}
# Do we have the correct plugin?
if ($user->{'_UserDB'}->{'Users_data_set'}) {
$server->log(LOG_INFO,"[MOD_USERS_DATA] Found plugin: '".$user->{'_UserDB'}->{'Name'}."'");
# Set user traffic usage
my $res = $user->{'_UserDB'}->{'Users_data_set'}($server,$user,
'mod_feature_user_stats','CurrentMonthTotalTraffic',
$currentUsage->{'TotalDataUsage'}
);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_USERS_DATA] Failed to store current month traffic usage for user '"
.$user->{'Username'}."'");
return MOD_RES_SKIP;
}
# Set user uptime usage
$res = $user->{'_UserDB'}->{'Users_data_set'}($server,$user,
'mod_feature_user_stats','CurrentMonthTotalUptime',
$currentUsage->{'TotalSessionTime'}
);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_USERS_DATA] Failed to store current month uptime usage for user '"
.$user->{'Username'}."'");
return MOD_RES_SKIP;
}
# Set NAS-Identifier
if (defined(my $NASIdentifier = $packet->rawattr('NAS-Identifier'))) {
$res = $user->{'_UserDB'}->{'Users_data_set'}($server,$user,
'mod_feature_user_stats','NAS-Identifier',
$NASIdentifier
);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_USERS_DATA] Failed to store NAS-Identifier for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
}
}
# Set Framed-IP-Address
if (defined(my $framedIPAddress = $packet->rawattr('Framed-IP-Address'))) {
$res = $user->{'_UserDB'}->{'Users_data_set'}($server,$user,
'mod_feature_user_stats','Framed-IP-Address',
$framedIPAddress
);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_USERS_DATA] Failed to store Framed-IP-Address for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
}
}
# Set user FUP state
# NK: Perhaps this should be moved to the mod_feature_fup module?
my $fupState = $user->{'AttributeConditionalVariables'}->{"SMRadius_FUP"};
if (defined($fupState)) {
$fupState = $fupState->[0];
} else {
$fupState = "-1";
}
$res = $user->{'_UserDB'}->{'Users_data_set'}($server,$user,
'mod_feature_fup','State',
$fupState
);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_USERS_DATA] Failed to store FUP state for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
}
}
return MOD_RES_ACK;
}
1;
# vim: ts=4
# Validity support
# Copyright (C) 2007-2009, AllWorldIT
#
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
......@@ -28,12 +28,10 @@ use DateTime;
use Date::Parse;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
use base qw(Exporter);
our @EXPORT = qw(
);
@EXPORT_OK = qw(
our @EXPORT_OK = qw(
);
......@@ -41,15 +39,17 @@ our (@ISA,@EXPORT,@EXPORT_OK);
our $pluginInfo = {
Name => "User Validity Feature",
Init => \&init,
# Authentication hook
'Feature_Post-Authentication_hook' => \&checkValidity,
'Feature_Post-Accounting_hook' => \&checkValidity
};
# Some constants
my $VALID_FROM_KEY = 'SMRadius-Validity-ValidFrom';
my $VALID_TO_KEY = 'SMRadius-Validity-ValidTo';
my $VALID_WINDOW_KEY = 'SMRadius-Validity-ValidWindow';
## @internal
......@@ -72,45 +72,70 @@ sub checkValidity
{
my ($server,$user,$packet) = @_;
# We cannot cap a user if we don't have a UserDB module can we? no userdb, no validity?
return MOD_RES_SKIP if (!defined($user->{'_UserDB'}->{'Name'}));
# Skip MAC authentication
return MOD_RES_SKIP if ($user->{'_UserDB'}->{'Name'} eq "SQL User Database (MAC authentication)");
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] POST AUTH HOOK");
my ($validFrom,$validTo);
my ($validFrom,$validTo,$validWindow);
# Get validity start date
if (defined($user->{'Attributes'}->{$VALID_FROM_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] '".$VALID_FROM_KEY."' is defined");
# Operator: ==
if (defined($user->{'Attributes'}->{$VALID_FROM_KEY}->{'=='})) {
# Operator: :=
if (defined($user->{'Attributes'}->{$VALID_FROM_KEY}->{':='})) {
# Is it formatted as a date?
if ($user->{'Attributes'}->{$VALID_FROM_KEY}->{'=='}->{'Value'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
$validFrom = $user->{'Attributes'}->{$VALID_FROM_KEY}->{'=='}->{'Value'};
if ($user->{'Attributes'}->{$VALID_FROM_KEY}->{':='}->{'Value'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
$validFrom = $user->{'Attributes'}->{$VALID_FROM_KEY}->{':='}->{'Value'};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_FROM_KEY}->{'=='}->{'Value'}.
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_FROM_KEY}->{':='}->{'Value'}.
"' is NOT in ISO standard format 'YYYY-MM-DD'");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] No valid operators for attribute '$VALID_FROM_KEY'");
}
}
} # if (defined($user->{'Attributes'}->{$VALID_FROM_KEY}->{':='})) {
} # if (defined($user->{'Attributes'}->{$VALID_FROM_KEY})) {
# Get validity end date
if (defined($user->{'Attributes'}->{$VALID_TO_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] '".$VALID_TO_KEY."' is defined");
# Operator: ==
if (defined($user->{'Attributes'}->{$VALID_TO_KEY}->{'=='})) {
# Operator: :=
if (defined($user->{'Attributes'}->{$VALID_TO_KEY}->{':='})) {
# Is it formatted as a date?
if ($user->{'Attributes'}->{$VALID_TO_KEY}->{'=='}->{'Value'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
$validTo = $user->{'Attributes'}->{$VALID_TO_KEY}->{'=='}->{'Value'};
if ($user->{'Attributes'}->{$VALID_TO_KEY}->{':='}->{'Value'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
$validTo = $user->{'Attributes'}->{$VALID_TO_KEY}->{':='}->{'Value'};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_TO_KEY}->{'=='}->{'Value'}.
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_TO_KEY}->{':='}->{'Value'}.
"' is NOT an ISO standard format 'YYYY-MM-DD'");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] No valid operators for attribute '$VALID_TO_KEY'");
}
}
} # if (defined($user->{'Attributes'}->{$VALID_TO_KEY}->{':='})) {
} # if (defined($user->{'Attributes'}->{$VALID_TO_KEY})) {
# Get validity window
if (defined($user->{'Attributes'}->{$VALID_WINDOW_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] '".$VALID_WINDOW_KEY."' is defined");
# Operator: :=
if (defined($user->{'Attributes'}->{$VALID_WINDOW_KEY}->{':='})) {
# Is it a number?
if ($user->{'Attributes'}->{$VALID_WINDOW_KEY}->{':='}->{'Value'} =~ /^\d+$/) {
$validWindow = $user->{'Attributes'}->{$VALID_WINDOW_KEY}->{':='}->{'Value'};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_WINDOW_KEY}->{':='}->{'Value'}.
"' is NOT an integer");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] No valid operators for attribute '$VALID_WINDOW_KEY'");
} # if (defined($user->{'Attributes'}->{$VALID_WINDOW_KEY}->{':='})) {
} # if (defined($user->{'Attributes'}->{$VALID_WINDOW_KEY})) {
# Now ...
......@@ -121,37 +146,67 @@ sub checkValidity
if (defined($validFrom)) {
# Convert string to datetime
my $validFrom_unixtime = str2time($validFrom);
my $validFrom_unixtime = str2time($validFrom,$server->{'smradius'}->{'event_timezone'});
if (!defined($validFrom_unixtime)) {
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] Date conversion failed on '".$validFrom."'");
$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] Date conversion failed on '%s'",$validFrom);
# If current time before start of valid pariod
} elsif ($now < $validFrom_unixtime) {
my $pretty_dt = DateTime->from_epoch( epoch => $validFrom_unixtime )->strftime('%Y-%m-%d %H:%M:%S');
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid start date: '".$pretty_dt."', rejecting");
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid start date: '%s', rejecting",$pretty_dt);
# Date not within valid period, must be disconnected
return MOD_RES_NACK;
}
}
} # if (!defined($validFrom_unixtime)) {
} # if (defined($validFrom)) {
# Do we have an end date?
if (defined($validTo)) {
# Convert string to datetime
my $validTo_unixtime = str2time($validTo);
my $validTo_unixtime = str2time($validTo,$server->{'smradius'}->{'event_timezone'});
if (!defined($validTo_unixtime)) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Date conversion failed on '".$validTo."'");
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Date conversion failed on '%s'",$validTo);
# If current time after start of valid pariod
} elsif ($now > $validTo_unixtime) {
my $pretty_dt = DateTime->from_epoch( epoch => $validTo_unixtime )->strftime('%Y-%m-%d %H:%M:%S');
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid end date: '".$pretty_dt."', rejecting");
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid end date: '%s', rejecting",$pretty_dt);
# Date not within valid period, must be disconnected
return MOD_RES_NACK;
}
} # if (!defined($validTo_unixtime)) {
} # if (defined($validTo)) {
# Do we have a validity window
if (defined($validWindow)) {
# Check first if we have the ability to support this feature
if (defined($user->{'_UserDB'}->{'Users_data_get'})) {
# Fetch users_data for first login
if (defined(my $res = $user->{'_UserDB'}->{'Users_data_get'}($server,$user,'global','FirstLogin'))) {
# Check if this user should be disconnected
if (defined($validWindow) && defined($res)) {
my $validUntil = $validWindow + $res->{'Value'};
# If current time after start of valid pariod
if ($now > $validUntil) {
my $pretty_dt = DateTime->from_epoch( epoch => $validUntil )->strftime('%Y-%m-%d %H:%M:%S');
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid window end date: '%s', ".
"rejecting",$pretty_dt);
# Date not within valid window, must be disconnected
return MOD_RES_NACK;
}
}
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] No users_data 'global/FirstLogin' found for user '%s'",
$user->{'Username'});
} # if (defined(my $res = $module->{'Users_data_get'}($server,$user,'global','FirstLogin'))) {
} else {
$server->log(LOG_WARN,"[MOD_FEATURE_VALIDITY] UserDB module '%s' does not support 'users_data'. Therefore no ".
"support for Validity Window feature",$user->{'_UserDB'}->{'Name'});
} # if (defined($user->{'_UserDB'}->{'Users_data_get'})) {
}
return MOD_RES_ACK;
......
# SQL config database support
# Copyright (C) 2007-2009, AllWorldIT
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -23,7 +23,10 @@ use warnings;
# Modules we need
use smradius::constants;
use smradius::logging;
use awitpt::db::dblayer;
use AWITPT::DB::DBLayer;
use AWITPT::Cache;
use AWITPT::NetIP;
use AWITPT::Util;
use smradius::util;
use smradius::attributes;
......@@ -47,6 +50,7 @@ our $pluginInfo = {
Config_get => \&getConfig,
};
# Module config
my $config;
......@@ -164,15 +168,16 @@ sub getConfig
my $realmID;
# Get default realm ID
$server->log(LOG_DEBUG,"Processing DEFAULT realm attributes");
my $sth = DBSelect($config->{'get_config_realm_id_query'},$realmName);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get default realm ID: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get default realm ID: ".AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Set realm ID
my $row;
if ($sth->rows == 1) {
$row = hashifyLCtoMC($sth->fetchrow_hashref(),qw(ID));
$row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID));
$realmID = $row->{'ID'};
}
DBFreeRes($sth);
......@@ -181,42 +186,46 @@ sub getConfig
if (defined($realmID)) {
$sth = DBSelect($config->{'get_config_realm_attributes_query'},$realmID);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get default realm config attributes: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get default realm config attributes: ".AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Add any default realm attributes to config attributes
while (my $row = $sth->fetchrow_hashref()) {
processConfigAttribute($server,$user->{'ConfigAttributes'},hashifyLCtoMC($row,qw(Name Operator Value)));
processConfigAttribute($server,$user,hashifyLCtoMC($row, qw(Name Operator Value)));
}
DBFreeRes($sth);
}
# Extract realm from username
if (defined($user->{'Username'}) && $user->{'Username'} =~ /^\S+@(\S+)$/) {
$realmName = $1;
if (defined($user->{'Username'}) && $user->{'Username'} =~ /^\S+(?:@(\S+))?$/) {
my $userRealm = $1 // "";
$server->log(LOG_DEBUG,"Processing attributes for realm '$userRealm'");
$sth = DBSelect($config->{'get_config_realm_id_query'},$realmName);
$sth = DBSelect($config->{'get_config_realm_id_query'},$userRealm);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get user realm config attributes: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get realm config attributes: ".AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Fetch realm ID
if ($sth->rows == 1) {
$row = hashifyLCtoMC($sth->fetchrow_hashref(),qw(ID));
$row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID));
$realmID = $row->{'ID'};
DBFreeRes($sth);
# User realm attributes
$sth = DBSelect($config->{'get_config_realm_attributes_query'},$realmID);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get user realm config attributes: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get realm config attributes: ".AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Add any realm attributes to config attributes
while (my $row = $sth->fetchrow_hashref()) {
processConfigAttribute($server,$user->{'ConfigAttributes'},hashifyLCtoMC($row,qw(Name Operator Value)));
processConfigAttribute($server,$user,hashifyLCtoMC($row, qw(Name Operator Value)));
}
DBFreeRes($sth);
$realmName = $userRealm;
}
}
......@@ -226,44 +235,80 @@ sub getConfig
return MOD_RES_NACK;
}
$server->log(LOG_DEBUG,"Realm '$realmName' has ID '$realmID'");
# Get client name
my ($clientID,$res);
$sth = DBSelect($config->{'get_config_accesslist_query'},$realmID);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get config attributes: ".awitpt::db::dblayer::Error());
return MOD_RES_NACK;
}
# Check if we know this client
my @accessList;
while (my $row = $sth->fetchrow_hashref()) {
$res = hashifyLCtoMC($row,qw(AccessList ID));
# Split off allowed sources, comma separated
@accessList = ();
@accessList = split(',',$res->{'AccessList'});
# Loop with what we get and check if we have match
foreach my $ip (@accessList) {
if ($server->{'server'}{'peeraddr'} eq $ip) {
$clientID = $res->{'ID'};
last;
my $clientID;
# Check Cache
my $doCheck = 1;
my ($cres,$val) = cacheGetComplexKeyPair('mod_config_sql',"access/".$server->{'server'}{'peeraddr'});
if (defined($val)) {
# Check if cache expired
if ($user->{'_Internal'}->{'Timestamp-Unix'} - $val->{'timestamp'} < 60) {
# Check if we were allowed access
if (defined($val->{'allowed'})) {
$clientID = $val->{'allowed'};
$server->log(LOG_INFO,"(CACHED) Got client ID '$clientID' from cache, bypassing accesslist check");
$doCheck = 0;
} else {
$server->log(LOG_INFO,"(CACHED) Peer Address '".$server->{'server'}{'peeraddr'}."' not found in access list");
}
}
}
DBFreeRes($sth);
if (!defined($clientID)) {
$server->log(LOG_ERR,"Peer Address '".$server->{'server'}{'peeraddr'}."' not found in access list");
return MOD_RES_NACK;
# Do check
if ($doCheck) {
$server->log(LOG_DEBUG,"Processing access list for realm '$realmName'");
$sth = DBSelect($config->{'get_config_accesslist_query'},$realmID);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get config attributes: ".AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Grab peer address object
my $peerAddrObj = AWITPT::NetIP->new($server->{'server'}{'peeraddr'});
# Check if we know this client
my @accessList;
while (my $row = $sth->fetchrow_hashref()) {
my $res = hashifyLCtoMC($row, qw(AccessList ID));
# Split off allowed sources, comma separated
@accessList = ();
@accessList = split(',',$res->{'AccessList'});
# Loop with what we get and check if we have match
foreach my $range (@accessList) {
my $rangeObj = AWITPT::NetIP->new($range);
# Check for match
if ($peerAddrObj->is_within($rangeObj)) {
$clientID = $res->{'ID'};
$server->log(LOG_INFO,"(SETCACHE) Got client ID '$clientID' from DB");
last;
}
}
}
DBFreeRes($sth);
if (!defined($clientID)) {
$server->log(LOG_NOTICE,"Peer Address '".$server->{'server'}{'peeraddr'}."' not found in access list");
return MOD_RES_NACK;
}
# Setup cached data
my %cacheData;
$cacheData{'allowed'} = $clientID;
$cacheData{'timestamp'} = $user->{'_Internal'}->{'Timestamp-Unix'};
cacheStoreComplexKeyPair('mod_config_sql',"access/".$server->{'server'}{'peeraddr'},\%cacheData);
}
# Get client attributes
$server->log(LOG_DEBUG,"Processing client attributes for '$clientID'");
if (defined($clientID)) {
my $sth = DBSelect($config->{'get_config_client_attributes_query'},$clientID);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get default config attributes: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get default config attributes: ".AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Add to config attributes
while (my $row = $sth->fetchrow_hashref()) {
processConfigAttribute($server,$user->{'ConfigAttributes'},hashifyLCtoMC($row,qw(Name Operator Value)));
processConfigAttribute($server,$user,hashifyLCtoMC($row, qw(Name Operator Value)));
}
DBFreeRes($sth);
}
......
# Topup support
# Copyright (C) 2007-2009, AllWorldIT
#
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
......@@ -23,23 +23,23 @@ use warnings;
# Modules we need
use smradius::constants;
use smradius::logging;
use awitpt::db::dblayer;
use AWITPT::DB::DBLayer;
use AWITPT::Util;
use smradius::util;
use smradius::attributes;
use POSIX qw(ceil);
use DateTime;
use Date::Parse;
use Math::BigInt;
use Math::BigFloat;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
use base qw(Exporter);
our @EXPORT = qw(
);
@EXPORT_OK = qw(
our @EXPORT_OK = qw(
);
......@@ -50,15 +50,26 @@ our $pluginInfo = {
Init => \&init,
# Cleanup run by smadmin
CleanupOrder => 80,
Cleanup => \&cleanup,
# User database
Config_get => \&getTopups
Config_get => \&getTopups,
# Topups
Feature_Config_Topop_add => \&addTopup,
};
# Module config
my $config;
# Helper functions
sub _isTrafficTopup { my $val = shift; return ($val & 1) == 1; }
sub _isUptimeTopup { my $val = shift; return ($val & 2) == 2; }
sub _isAutoTopup { my $val = shift; return ($val & 4) == 4; }
## @internal
# Initialize module
sub init
......@@ -101,33 +112,65 @@ sub init
@TP@users
WHERE
@TP@topups.UserID = @TP@users.ID
AND @TP@topups.ValidFrom >= ?
AND @TP@topups.ValidFrom = ?
AND @TP@topups.ValidTo >= ?
AND @TP@topups.Depleted = 0
AND @TP@users.Username = ?
';
$config->{'topups_add_query'} = '
INSERT INTO
@TP@topups
(
UserID,
Timestamp,
ValidFrom,
ValidTo,
Type,
Value,
Depleted
)
VALUES
(
%{user.ID},
%{query.Timestamp},
%{query.ValidFrom},
%{query.ValidTo},
%{query.Type},
%{query.Value},
%{query.Depleted}
)
';
# Setup SQL queries
if (defined($scfg->{'mod_topups_sql'})) {
if (defined($scfg->{'mod_config_sql_topups'})) {
# Pull in queries
if (defined($scfg->{'mod_topups_sql'}->{'get_topups_summary_query'}) &&
$scfg->{'mod_topups_sql'}->{'get_topups_summary_query'} ne "") {
if (ref($scfg->{'mod_topups_sql'}->{'get_topups_summary_query'}) eq "ARRAY") {
$config->{'get_topups_summary_query'} = join(' ',@{$scfg->{'mod_config_sql'}->{'get_topups_summary_query'}});
if (defined($scfg->{'mod_config_sql_topups'}->{'get_topups_summary_query'}) &&
$scfg->{'mod_config_sql_topups'}->{'get_topups_summary_query'} ne "") {
if (ref($scfg->{'mod_config_sql_topups'}->{'get_topups_summary_query'}) eq "ARRAY") {
$config->{'get_topups_summary_query'} = join(' ',@{$scfg->{'mod_config_sql_topups'}->{'get_topups_summary_query'}});
} else {
$config->{'get_topups_summary_query'} = $scfg->{'mod_config_sql'}->{'get_topups_summary_query'};
$config->{'get_topups_summary_query'} = $scfg->{'mod_config_sql_topups'}->{'get_topups_summary_query'};
}
}
if (defined($scfg->{'mod_topups_sql'}->{'get_config_query'}) &&
$scfg->{'mod_topups_sql'}->{'get_config_query'} ne "") {
if (ref($scfg->{'mod_topups_sql'}->{'get_config_query'}) eq "ARRAY") {
$config->{'get_config_query'} = join(' ',@{$scfg->{'mod_config_sql'}->{'get_config_query'}});
if (defined($scfg->{'mod_config_sql_topups'}->{'get_topups_query'}) &&
$scfg->{'mod_config_sql_topups'}->{'get_topups_query'} ne "") {
if (ref($scfg->{'mod_config_sql_topups'}->{'get_topups_query'}) eq "ARRAY") {
$config->{'get_topups_query'} = join(' ',@{$scfg->{'mod_config_sql_topups'}->{'get_topups_query'}});
} else {
$config->{'get_topups_query'} = $scfg->{'mod_config_sql_topups'}->{'get_topups_query'};
}
}
if (defined($scfg->{'mod_config_sql_topups'}->{'topups_add_query'}) &&
$scfg->{'mod_config_sql_topups'}->{'topups_add_query'} ne "") {
if (ref($scfg->{'mod_config_sql_topups'}->{'topups_add_query'}) eq "ARRAY") {
$config->{'topups_add_query'} = join(' ',@{$scfg->{'mod_config_sql_topups'}->{'topups_add_query'}});
} else {
$config->{'get_config_query'} = $scfg->{'mod_config_sql'}->{'get_config_query'};
$config->{'topups_add_query'} = $scfg->{'mod_config_sql_topups'}->{'topups_add_query'};
}
}
}
}
......@@ -145,6 +188,25 @@ sub getTopups
{
my ($server,$user,$packet) = @_;
# Fetch all summaries
my $trafficSummaries = {
'traffic' => 0,
'uptime' => 0,
'traffic-topup' => 0,
'uptime-topup' => 0,
'traffic-auto' => 0,
'uptime-auto' => 0,
};
# Check to see if we have a username
my $username = $user->{'Username'};
# Skip this module if we don't have a username
if (!defined($username)) {
return MOD_RES_SKIP;
}
# Make time for month begin
my $now = DateTime->from_epoch( epoch => $user->{'_Internal'}->{'Timestamp-Unix'} );
my $thisMonth = DateTime->new( year => $now->year, month => $now->month, day => 1 );
......@@ -153,109 +215,38 @@ sub getTopups
my $periodKey = $thisMonth->strftime("%Y-%m");
# Query database
my $sth = DBSelect($config->{'get_topups_summary_query'},$periodKey,$packet->attr('User-Name'));
my $sth = DBSelect($config->{'get_topups_summary_query'},$periodKey,$username);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get topup information: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get topup information: %s",AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Fetch all summaries
my (@trafficSummary,@uptimeSummary);
while (my $row = $sth->fetchrow_hashref()) {
$row = hashifyLCtoMC($row, qw(Balance Type ID));
if ($row->{'Type'} == 1) {
# Add to traffic summary list
push(@trafficSummary, { Value => $row->{'Balance'}, ID => $row->{'ID'} });
}
if ($row->{'Type'} == 2) {
# Add to uptime summary list
push(@uptimeSummary, { Value => $row->{'Balance'}, ID => $row->{'ID'} });
}
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(Balance Type ID))) {
_trafficSummaryAdd($trafficSummaries,$row,'Balance');
}
DBFreeRes($sth);
# Query database
$sth = DBSelect($config->{'get_topups_query'},$thisMonth,$now,$packet->attr('User-Name'));
$sth = DBSelect($config->{'get_topups_query'},$thisMonth->ymd,$now->ymd,$username);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get topup information: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"Failed to get topup information: %s",AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
# Fetch all new topups
# Fetch all new topups
my (@trafficTopups,@uptimeTopups);
while (my $row = $sth->fetchrow_hashref()) {
$row = hashifyLCtoMC($row, qw(ID Type Value));
if ($row->{'Type'} == 1) {
# Add topup to traffic array
push(@trafficTopups, { Value => $row->{'Value'}, ID => $row->{'ID'} });
}
if ($row->{'Type'} == 2) {
# Add topup to uptime array
push(@uptimeTopups, { Value => $row->{'Value'}, ID => $row->{'ID'} });
}
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID Type Value))) {
_trafficSummaryAdd($trafficSummaries,$row,'Value');
}
DBFreeRes($sth);
# Add up traffic
my $totalTopupTraffic = 0;
# Traffic topups..
foreach my $topup (@trafficTopups) {
# Use only if numeric
if ($topup->{'Value'} =~ /^[0-9]+$/) {
$totalTopupTraffic += $topup->{'Value'};
} else {
$server->log(LOG_DEBUG,"[MOD_CONFIG_SQL_TOPUPS] Topup with ID '".niceUndef($topup->{'ID'}).
"' is not a numeric value");
return MOD_RES_NACK;
}
}
# Traffic summaries..
foreach my $summary (@trafficSummary) {
# Use only if numeric
if ($summary->{'Value'} =~ /^[0-9]+$/) {
$totalTopupTraffic += $summary->{'Value'};
} else {
$server->log(LOG_DEBUG,"[MOD_CONFIG_SQL_TOPUPS] Topup with ID '".niceUndef($summary->{'ID'}).
"' is not a numeric value");
return MOD_RES_NACK;
}
}
# Add up uptime
my $totalTopupUptime = 0;
# Uptime topups..
foreach my $topup (@uptimeTopups) {
# Use only if numeric
if ($topup->{'Value'} =~ /^[0-9]+$/) {
$totalTopupUptime += $topup->{'Value'};
} else {
$server->log(LOG_DEBUG,"[MOD_CONFIG_SQL_TOPUPS] Topup with ID '".niceUndef($topup->{'ID'}).
"' is not a numeric value");
return MOD_RES_NACK;
}
}
# Uptime summaries..
foreach my $summary (@uptimeSummary) {
# Use only if numeric
if ($summary->{'Value'} =~ /^[0-9]+$/) {
$totalTopupUptime += $summary->{'Value'};
} else {
$server->log(LOG_DEBUG,"[MOD_CONFIG_SQL_TOPUPS] Topup with ID '".niceUndef($summary->{'ID'}).
"' is not a numeric value");
return MOD_RES_NACK;
}
}
# Process traffic topups
processConfigAttribute($server,$user->{'ConfigAttributes'},{ 'Name' => 'SMRadius-Capping-Traffic-Topup',
'Operator' => ':=', 'Value' => $totalTopupTraffic });
# Process uptime topups
processConfigAttribute($server,$user->{'ConfigAttributes'},{ 'Name' => 'SMRadius-Capping-Uptime-Topup',
'Operator' => ':=', 'Value' => $totalTopupUptime });
# Save configuration for the user
processConfigAttribute($server,$user,{ 'Name' => 'SMRadius-Capping-Traffic-Topup',
'Operator' => ':=', 'Value' => $trafficSummaries->{'traffic'} });
processConfigAttribute($server,$user,{ 'Name' => 'SMRadius-Capping-Uptime-Topup',
'Operator' => ':=', 'Value' => $trafficSummaries->{'uptime'} });
processConfigAttribute($server,$user,{ 'Name' => 'SMRadius-Capping-Traffic-AutoTopup',
'Operator' => ':=', 'Value' => $trafficSummaries->{'traffic-auto'} });
processConfigAttribute($server,$user,{ 'Name' => 'SMRadius-Capping-Uptime-AutoTopup',
'Operator' => ':=', 'Value' => $trafficSummaries->{'uptime-auto'} });
return MOD_RES_ACK;
}
......@@ -264,195 +255,303 @@ sub getTopups
# Topup summary function
sub cleanup
{
my ($server) = @_;
my ($server,$runForDate) = @_;
# The datetime now
my $now = DateTime->from_epoch(epoch => $runForDate)->set_time_zone($server->{'smradius'}->{'event_timezone'});
# Use truncate to set all values after 'month' to their default values
my $thisMonth = $now->clone()->truncate( to => "month" );
# Format this month period key
my $curPeriodKey = $thisMonth->strftime("%Y-%m");
# Last month..
my $lastMonth = $thisMonth->clone()->subtract( months => 1 );
my $prevPeriodKey = $lastMonth->strftime("%Y-%m");
# Next month..
my $nextMonth = $thisMonth->clone()->add( months => 1 );
my $unix_nextMonth = $nextMonth->epoch();
# Get a timestamp for this user
my $depletedTimestamp = $now->strftime('%Y-%m-%d %H:%M:%S');
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Generating list of users");
# TODO - be more dynamic, we may not be using SQL users
# Get all usernames
my $sth = DBSelect('SELECT ID, Username FROM @TP@users');
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select from users: ".
awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select users: ".
AWITPT::DB::DBLayer::error());
return;
}
# Create hash of usernames
my %users;
while (my $user = $sth->fetchrow_hashref()) {
$user = hashifyLCtoMC($user, qw(ID Username));
while (my $user = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID Username))) {
$users{$user->{'ID'}} = $user->{'Username'};
}
# Finished for now
DBFreeRes($sth);
# The datetime now
my $now = DateTime->now;
# Make datetime
my $thisMonth = DateTime->new( year => $now->year, month => $now->month, day => 1 );
# Start of multiple queries
DBBegin();
# Get begin date of last month
my ($prevYear,$prevMonth);
if ($now->month == 1) {
$prevYear = $now->year - 1;
$prevMonth = 12;
} else {
$prevYear = $now->year;
$prevMonth = $now->month - 1;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Removing all old topup summaries");
# Remove topup summaries
# NK: MYSQL SPECIFIC
$sth = DBDo('
DELETE FROM
@TP@topups_summary
WHERE
STR_TO_DATE(PeriodKey,"%Y-%m") >= ?',
$curPeriodKey
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to delete topup summaries: ".
AWITPT::DB::DBLayer::error());
DBRollback();
return;
}
my $lastMonth = DateTime->new( year => $prevYear, month => $prevMonth, day => 1 );
# Get begin date of next month
my ($folYear,$folMonth);
if ($now->month == 12) {
$folYear = $now->year + 1;
$folMonth = 1;
} else {
$folYear = $now->year;
$folMonth = $now->month + 1;
# Undeplete topups
$sth = DBDo('
UPDATE
@TP@topups
SET
Depleted = 0,
SMAdminDepletedOn = NULL
WHERE
SMAdminDepletedOn >= ?', $thisMonth->ymd()
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to undeplete topups: ".AWITPT::DB::DBLayer::error());
DBRollback();
return;
}
my $nextMonth = DateTime->new( year => $folYear, month => $folMonth, day => 1 );
my $unix_nextMonth = $nextMonth->epoch();
# Start of multiple queries
DBBegin();
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Retrieving accounting summaries");
# Undeplete topup summaries
$sth = DBDo('
UPDATE
@TP@topups_summary
SET
Depleted = 0,
SMAdminDepletedOn = NULL
WHERE
SMAdminDepletedOn >= ?', $thisMonth->ymd()
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to retrieve accounting summaries: ".
AWITPT::DB::DBLayer::error());
DBRollback();
return;
}
# Loop through users
foreach my $userID (keys %users) {
my $userName = $users{$userID};
my $username = $users{$userID};
# TODO - in future we must be more dynamic, we may not be using SQL accunting
# Get traffic and uptime usage for last month
my $sth = DBSelect('
SELECT
Username,
SUM(AcctSessionTime) as AcctSessionTime,
SUM(AcctInputOctets) as AcctInputOctets,
SUM(AcctInputGigawords) as AcctInputGigawords,
SUM(AcctOutputOctets) as AcctOutputOctets,
SUM(AcctOutputGigawords) as AcctOutputGigawords
TotalInput,
TotalOutput,
TotalSessionTime
FROM
@TP@accounting
@TP@accounting_summary
WHERE
EventTimestamp > ?
PeriodKey = ?
AND Username = ?
GROUP BY
Username
',
$lastMonth,$userName
$prevPeriodKey,$username
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select accounting records: ".
awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select accounting summary record: ".
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
my $row = $sth->fetchrow_hashref();
if ($sth->rows > 0) {
$row = hashifyLCtoMC(
$row,
qw(Username AcctSessionTime AcctInputOctets AcctInputGigawords AcctOutputOctets AcctOutputGigawords)
);
}
# Our usage hash
my %usageTotals;
$usageTotals{'TotalSessionTime'} = Math::BigInt->new(0);
$usageTotals{'TotalDataUsage'} = Math::BigInt->new(0);
# Add up traffic
my $totalData = 0;
if (defined($row->{'AcctInputOctets'}) && $row->{'AcctInputOctets'} > 0) {
$totalData += $row->{'AcctInputOctets'} / 1024 / 1024;
}
if (defined($row->{'AcctInputGigawords'}) && $row->{'AcctInputGigawords'} > 0) {
$totalData += $row->{'AcctInputGigawords'} * 4096;
}
if (defined($row->{'AcctOutputOctets'}) && $row->{'AcctOutputOctets'} > 0) {
$totalData += $row->{'AcctOutputOctets'} / 1024 / 1024;
}
if (defined($row->{'AcctOutputGigawords'}) && $row->{'AcctOutputGigawords'} > 0) {
$totalData += $row->{'AcctOutputGigawords'} * 4096;
# Pull in usage and add up
if (my $row = hashifyLCtoMC($sth->fetchrow_hashref(),
qw(TotalSessionTime TotalInput TotalOutput)
)) {
# Look for session time
if (defined($row->{'TotalSessionTime'}) && $row->{'TotalSessionTime'} > 0) {
$usageTotals{'TotalSessionTime'}->badd($row->{'TotalSessionTime'});
}
# Add input usage if we have any
if (defined($row->{'TotalInput'}) && $row->{'TotalInput'} > 0) {
$usageTotals{'TotalDataUsage'}->badd($row->{'TotalInput'});
}
# Add output usage if we have any
if (defined($row->{'TotalOutput'}) && $row->{'TotalOutput'} > 0) {
$usageTotals{'TotalDataUsage'}->badd($row->{'TotalOutput'});
}
}
DBFreeRes($sth);
# Log the summary
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Username '%s', PeriodKey '%s', TotalSessionTime '%s', ".
" TotalDataUsage '%s'",$username,$prevPeriodKey,$usageTotals{'TotalSessionTime'}->bstr(),
$usageTotals{'TotalDataUsage'}->bstr(), $usageTotals{'TotalDataUsage'}->bstr());
# Get user traffic and uptime limits from group attributes
# FIXME - Support for realm config
$sth = DBSelect('
SELECT
@TP@group_attributes.Name, @TP@group_attributes.Operator, @TP@group_attributes.Value
FROM
@TP@group_attributes, @TP@users_to_groups
WHERE
@TP@group_attributes.GroupID = @TP@users_to_groups.GroupID
AND @TP@users_to_groups.UserID = ?
',
$userID
);
# Add up uptime
my $totalTime = 0;
if (defined($row->{'AcctSessionTime'}) && $row->{'AcctSessionTime'} > 0) {
$totalTime = $row->{'AcctSessionTime'} / 60;
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select group usage caps: ".
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
# Rounding up
my $totalTrafficUsage = ceil($totalData);
my $totalUptimeUsage = ceil($totalTime);
# Store limits in capRecord hash
my %capRecord;
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(Name Operator Value))) {
if (defined($row->{'Name'})) {
if ($row->{'Name'} eq 'SMRadius-Capping-Traffic-Limit') {
if (defined($row->{'Operator'}) && $row->{'Operator'} eq ':=') {
if (defined($row->{'Value'}) && $row->{'Value'} =~ /^[\d]+$/) {
$capRecord{'TrafficLimit'} = $row->{'Value'};
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => SMRadius-Capping-Traffic-Limit ".
"value invalid for user '".$username."'");
}
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Incorrect '".$row->{'Name'}."' operator '"
.$row->{'Operator'}."' used for user '".$username."'");
}
}
if ($row->{'Name'} eq 'SMRadius-Capping-Uptime-Limit') {
if (defined($row->{'Operator'}) && $row->{'Operator'} eq ':=') {
if (defined($row->{'Value'}) && $row->{'Value'} =~ /^[\d]+$/) {
$capRecord{'UptimeLimit'} = $row->{'Value'};
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => SMRadius-Capping-Uptime-Limit value ".
"invalid for user '".$username."'");
}
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Incorrect '".$row->{'Name'}."' operator '"
.$row->{'Operator'}."' used for user '".$username."'");
}
}
}
}
# Finished for now
DBFreeRes($sth);
# TODO?
# FIXME - support for groups and realm config?
# Get user traffic and uptime limits
# Get user traffic and uptime limits from user attributes
$sth = DBSelect('
SELECT
@TP@user_attributes.Name, @TP@user_attributes.Value
Name, Operator, Value
FROM
@TP@user_attributes, @TP@users
@TP@user_attributes
WHERE
@TP@users.Username = ?
UserID = ?
',
$userName
$userID
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select usage caps: ".
awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select user usage caps: ".
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
# Store limits in capRecord hash
my %capRecord;
while ($row = $sth->fetchrow_hashref()) {
$row = hashifyLCtoMC(
$row,
qw(Name Value)
);
if ($row->{'Name'} eq 'SMRadius-Capping-Traffic-Limit') {
$capRecord{'TrafficLimit'} = $row->{'Value'};
}
if ($row->{'Name'} eq 'SMRadius-Capping-Uptime-Limit') {
$capRecord{'UptimeLimit'} = $row->{'Value'};
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(Name Operator Value))) {
if (defined($row->{'Name'})) {
if ($row->{'Name'} eq 'SMRadius-Capping-Traffic-Limit') {
if (defined($row->{'Operator'}) && $row->{'Operator'} eq ':=') {
if (defined($row->{'Value'}) && $row->{'Value'} =~ /^[\d]+$/) {
$capRecord{'TrafficLimit'} = $row->{'Value'};
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => SMRadius-Capping-Traffic-Limit value ".
"invalid for user '".$username."'");
}
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Incorrect '".$row->{'Name'}."' operator '"
.$row->{'Operator'}."' used for user '".$username."'");
}
}
if ($row->{'Name'} eq 'SMRadius-Capping-Uptime-Limit') {
if (defined($row->{'Operator'}) && $row->{'Operator'} eq ':=') {
if (defined($row->{'Value'}) && $row->{'Value'} =~ /^[\d]+$/) {
$capRecord{'UptimeLimit'} = $row->{'Value'};
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => SMRadius-Capping-Uptime-Limit value ".
"invalid for user '".$username."'");
}
} else {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Incorrect '".$row->{'Name'}."' operator '"
.$row->{'Operator'}."' used for user '".$username."'");
}
}
}
}
# Finished for now
DBFreeRes($sth);
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => CAP: ".
"SMRadius-Capping-Traffic-Limit '%s', SMRadius-Capping-Uptime-Limit '%s'",
$capRecord{'TrafficLimit'} ? $capRecord{'TrafficLimit'} : "-",
$capRecord{'UptimeLimit'} ? $capRecord{'TrafficLimit'} : "-"
);
# Get users topups that are still valid from topups_summary, must not be depleted
my $prevPeriodKey = $lastMonth->strftime("%Y-%m");
$sth = DBSelect('
SELECT
@TP@topups_summary.TopupID,
@TP@topups_summary.Balance,
@TP@topups.Value,
@TP@topups.ValidTo,
@TP@topups.Type
FROM
@TP@topups_summary, @TP@topups, @TP@users
@TP@topups_summary, @TP@topups
WHERE
@TP@topups_summary.Depleted = 0
AND @TP@topups.Depleted = 0
AND @TP@topups_summary.TopupID = @TP@topups.ID
AND @TP@users.ID = @TP@topups.UserID
AND @TP@users.Username = ?
AND @TP@topups.UserID = ?
AND @TP@topups_summary.PeriodKey = ?
ORDER BY
@TP@topups.Timestamp
',
$userName, $prevPeriodKey
$userID, $prevPeriodKey
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select topup summaries: ".
awitpt::db::dblayer::Error());
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
......@@ -460,31 +559,42 @@ sub cleanup
# Add previous valid topups to lists
my @trafficSummary = ();
my @uptimeSummary = ();
while (my $row = $sth->fetchrow_hashref()) {
$row = hashifyLCtoMC(
$row,
qw(TopupID Balance Value ValidTo Type)
);
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(TopupID Balance Value ValidTo Type))) {
if (defined($row->{'ValidTo'})) {
# Convert string to unix time
my $unix_validTo = str2time($row->{'ValidTo'});
if ($row->{'Type'} == 1) {
push(@trafficSummary, {
my $unix_validTo = str2time($row->{'ValidTo'},$server->{'smradius'}->{'event_timezone'});
# Process traffic topup
if (_isTrafficTopup($row->{'Type'})) {
push(@trafficSummary, {
TopupID => $row->{'TopupID'},
Balance => $row->{'Balance'},
Value => $row->{'Value'},
ValidTo => $unix_validTo,
Type => $row->{'Type'}
});
} elsif ($row->{'Type'} == 2) {
push(@uptimeSummary, {
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC SUMMARY TOPUP: ".
"ID '%s', Balance '%s', ValidTo '%s'",
$row->{'TopupID'},
$row->{'Balance'},
DateTime->from_epoch(epoch => $unix_validTo)->strftime("%F")
);
# Process uptime topup
} elsif (_isUptimeTopup($row->{'Type'})) {
push(@uptimeSummary, {
TopupID => $row->{'TopupID'},
Balance => $row->{'Balance'},
Value => $row->{'Value'},
ValidTo => $unix_validTo,
Type => $row->{'Type'}
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME SUMMARY TOPUP: ".
"ID '%s', Balance '%s', ValidTo '%s'",
$$row->{'TopupID'},
$row->{'Balance'},
DateTime->from_epoch(epoch => $unix_validTo)->strftime("%F")
);
}
}
}
......@@ -496,52 +606,60 @@ sub cleanup
# Get topups from last month
$sth = DBSelect('
SELECT
@TP@topups.ID, @TP@topups.Value, @TP@topups.Type, @TP@topups.ValidTo
ID, Value, Type, ValidTo
FROM
@TP@topups, @TP@users
@TP@topups
WHERE
@TP@topups.Depleted = 0
AND @TP@topups.UserID = @TP@users.ID
AND @TP@users.Username = ?
AND @TP@topups.ValidFrom <= ?
AND @TP@topups.ValidTo >= ?
Depleted = 0
AND UserID = ?
AND ValidFrom = ?
AND ValidTo >= ?
ORDER BY
@TP@topups.Timestamp
Timestamp
',
$userName,$lastMonth,$thisMonth
$userID,$lastMonth,$thisMonth
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select topups: ".
awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to select topups: ".AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
# Loop with the topups and push them into arrays
my (@trafficTopups,@uptimeTopups);
while (my $row = $sth->fetchrow_hashref()) {
$row = hashifyLCtoMC(
$row,
qw(ID Value Type ValidTo)
);
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID Value Type ValidTo))) {
# Convert string to unix time
my $unix_validTo = str2time($row->{'ValidTo'});
my $unix_validTo = str2time($row->{'ValidTo'},$server->{'smradius'}->{'event_timezone'});
# If this is a traffic topup ...
if ($row->{'Type'} == 1) {
if (_isTrafficTopup($row->{'Type'})) {
push(@trafficTopups, {
ID => $row->{'ID'},
Value => $row->{'Value'},
ValidTo => $unix_validTo
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC TOPUP: ".
"ID '%s', Balance '%s', ValidTo '%s'",
$row->{'ID'},
$row->{'Value'},
DateTime->from_epoch(epoch => $unix_validTo)->strftime("%F")
);
# Or a uptime topup...
} elsif ($row->{'Type'} == 2) {
} elsif (_isUptimeTopup($row->{'Type'})) {
push(@uptimeTopups, {
ID => $row->{'ID'},
Value => $row->{'Value'},
ValidTo => $unix_validTo
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME TOPUP: ".
"ID '%s', Balance '%s', ValidTo '%s'",
$row->{'ID'},
$row->{'Value'},
DateTime->from_epoch(epoch => $unix_validTo)->strftime("%F")
);
}
}
......@@ -550,46 +668,43 @@ sub cleanup
# List of summaries depleted
my @depletedSummary = ();
# Summaries to be edited/repeated
my @summaryTopups = ();
# List of depleted topups, looping through summaries may
# deplete a topup and topups table must be updated too
my @depletedTopups = ();
# Format this month period key
my $periodKey = $thisMonth->strftime("%Y-%m");
# Summaries to be edited/repeated
my @summaryTopups = ();
my $uptimeOverUsage = 0;
# Calculate excess usage if necessary
my $trafficOverUsage = 0;
if (defined($capRecord{'TrafficLimit'})) {
# Check traffic used against cap
$trafficOverUsage = $totalTrafficUsage - $capRecord{'TrafficLimit'};
# If there is no limit, this may be a prepaid user
} else {
$capRecord{'TrafficLimit'} = 0;
foreach my $prevTopup (@trafficSummary) {
$capRecord{'TrafficLimit'} += $prevTopup->{'Balance'};
}
foreach my $topup (@trafficTopups) {
$capRecord{'TrafficLimit'} += $topup->{'Value'};
}
$trafficOverUsage = $totalTrafficUsage - $capRecord{'TrafficLimit'};
if (defined($capRecord{'TrafficLimit'}) && $capRecord{'TrafficLimit'} > 0) {
$trafficOverUsage = $usageTotals{'TotalDataUsage'} - $capRecord{'TrafficLimit'};
} elsif (!(defined($capRecord{'TrafficLimit'}))) {
$trafficOverUsage = $usageTotals{'TotalDataUsage'};
}
# User has started using topup bandwidth..
if ($trafficOverUsage > 0) {
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC OVERAGE: $trafficOverUsage");
# Sort topups first expiring first
my @sortedTrafficSummary = sort { $a->{'ValidTo'} cmp $b->{'ValidTo'} } @trafficSummary;
# Loop with previous topups, setting them depleted or repeating as necessary
foreach my $summaryItem (@trafficSummary) {
foreach my $summaryItem (@sortedTrafficSummary) {
# Summary has not been used, if valid add to list to be repeated
if ($trafficOverUsage <= 0 && $summaryItem->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $summaryItem->{'TopupID'},
PeriodKey => $periodKey,
Balance => $summaryItem->{'Value'}
PeriodKey => $curPeriodKey,
Balance => $summaryItem->{'Balance'}
});
# This topup has not been touched and will be carried over
next;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC SUMMARY UNUSED: ".
"TOPUPID '%s', Balance '%s'",
$summaryItem->{'TopupID'},
$summaryItem->{'Balance'},
);
# Topup summary depleted
} elsif ($summaryItem->{'Balance'} <= $trafficOverUsage) {
push(@depletedSummary, $summaryItem->{'TopupID'});
......@@ -598,50 +713,90 @@ sub cleanup
# Excess traffic remaining
$trafficOverUsage -= $summaryItem->{'Balance'};
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC SUMMARY DEPLETED: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$summaryItem->{'TopupID'},
$summaryItem->{'Balance'},
$trafficOverUsage
);
# Topup summary still alive
} else {
my $trafficRemaining = $summaryItem->{'Balance'} - $trafficOverUsage;
if ($summaryItem->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $summaryItem->{'TopupID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $trafficRemaining
});
}
# All excess traffic has been "paid" for
$trafficOverUsage = 0;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC SUMMARY USAGE: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$summaryItem->{'TopupID'},
$trafficRemaining,
$trafficOverUsage
);
}
}
# Sort topups first expiring first
my @sortedTrafficTopups = sort { $a->{'ValidTo'} cmp $b->{'ValidTo'} } @trafficTopups;
# Loop with topups, setting them depleted or adding summary as necessary
foreach my $topup (@trafficTopups) {
foreach my $topup (@sortedTrafficTopups) {
# Topup has not been used, if valid add to summary
if ($trafficOverUsage <= 0 && $topup->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $topup->{'ID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $topup->{'Value'}
});
# This topup has not been touched and will be carried over
next;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC TOPUP UNUSED: ".
"TOPUPID '%s', Balance '%s'",
$topup->{'ID'},
$topup->{'Value'}
);
# Topup depleted
} elsif ($topup->{'Value'} <= $trafficOverUsage) {
push(@depletedTopups, $topup->{'ID'});
# Excess traffic remaining
$trafficOverUsage -= $topup->{'Value'};
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC TOPUP DEPLETED: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$topup->{'ID'},
$topup->{'Value'},
$trafficOverUsage
);
# Topup still alive
} else {
# Check if this summary exists in the list
my $trafficRemaining = $topup->{'Value'} - $trafficOverUsage;
if ($topup->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $topup->{'ID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $trafficRemaining
});
}
# All excess traffic has been "paid" for
$trafficOverUsage = 0;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC TOPUP USAGE: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$topup->{'ID'},
$trafficRemaining,
$trafficOverUsage
);
}
}
......@@ -653,9 +808,15 @@ sub cleanup
if ($summaryItem->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $summaryItem->{'TopupID'},
PeriodKey => $periodKey,
Balance => $summaryItem->{'Value'}
PeriodKey => $curPeriodKey,
Balance => $summaryItem->{'Balance'}
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC SUMMARY CARRY: ".
"TOPUPID '%s', Balance '%s'",
$summaryItem->{'TopupID'},
$summaryItem->{'Balance'}
);
}
}
# Check for topups
......@@ -664,43 +825,51 @@ sub cleanup
if ($topup->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $topup->{'ID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $topup->{'Value'}
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => TRAFFIC TOPUP CARRY: ".
"TOPUPID '%s', Balance '%s'",
$topup->{'ID'},
$topup->{'Value'}
);
}
}
}
if (defined($capRecord{'UptimeLimit'})) {
# Check traffic used against cap
$uptimeOverUsage = $totalUptimeUsage - $capRecord{'UptimeLimit'};
# If there is no limit, this may be a prepaid user
} else {
$capRecord{'UptimeLimit'} = 0;
foreach my $prevTopup (@uptimeSummary) {
$capRecord{'UptimeLimit'} += $prevTopup->{'Balance'};
}
foreach my $topup (@uptimeTopups) {
$capRecord{'UptimeLimit'} += $topup->{'Value'};
}
$uptimeOverUsage = $totalUptimeUsage - $capRecord{'UptimeLimit'};
# Calculate excess usage if necessary
my $uptimeOverUsage = 0;
if (defined($capRecord{'UptimeLimit'}) && $capRecord{'UptimeLimit'} > 0) {
$uptimeOverUsage = $usageTotals{'TotalSessionTime'} - $capRecord{'UptimeLimit'};
} elsif (!(defined($capRecord{'UptimeLimit'}))) {
$uptimeOverUsage = $usageTotals{'TotalSessionTime'};
}
# User has started using topup uptime..
if ($uptimeOverUsage > 0) {
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME OVERAGE: $uptimeOverUsage");
# Sort topups first expiring first
my @sortedUptimeSummary = sort { $a->{'ValidTo'} cmp $b->{'ValidTo'} } @uptimeSummary;
# Loop with previous topups, setting them depleted or repeating as necessary
foreach my $summaryItem (@uptimeSummary) {
foreach my $summaryItem (@sortedUptimeSummary) {
# Summary has not been used, if valid add to list to be repeated
if ($uptimeOverUsage <= 0 && $summaryItem->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $summaryItem->{'TopupID'},
PeriodKey => $periodKey,
Balance => $summaryItem->{'Value'}
PeriodKey => $curPeriodKey,
Balance => $summaryItem->{'Balance'}
});
# This topup has not been touched and will be carried over
next;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME SUMMARY UNUSED: ".
"TOPUPID '%s', Balance '%s'",
$summaryItem->{'TopupID'},
$summaryItem->{'Balance'},
);
# Topup summary depleted
} elsif ($summaryItem->{'Balance'} <= $uptimeOverUsage) {
push(@depletedSummary, $summaryItem->{'TopupID'});
......@@ -709,49 +878,86 @@ sub cleanup
# Excess uptime remaining
$uptimeOverUsage -= $summaryItem->{'Balance'};
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME SUMMARY DEPLETED: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$summaryItem->{'TopupID'},
$summaryItem->{'Balance'},
$uptimeOverUsage
);
# Topup summary still alive
} else {
my $uptimeRemaining = $summaryItem->{'Balance'} - $uptimeOverUsage;
if ($summaryItem->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $summaryItem->{'TopupID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $uptimeRemaining
});
}
# All excess uptime has been "paid" for
$uptimeOverUsage = 0;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME SUMMARY USAGE: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$summaryItem->{'TopupID'},
$uptimeRemaining,
$uptimeOverUsage
);
}
}
# Sort topups first expiring first
my @sortedUptimeTopups = sort { $a->{'ValidTo'} cmp $b->{'ValidTo'} } @uptimeTopups;
# Loop with topups, setting them depleted or adding summary as necessary
foreach my $topup (@uptimeTopups) {
foreach my $topup (@sortedUptimeTopups) {
# Topup has not been used, if valid add to summary
if ($uptimeOverUsage <= 0 && $topup->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $topup->{'ID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $topup->{'Value'}
});
# This topup has not been touched and will be carried over
next;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME TOPUP UNUSED: ".
"TOPUPID '%s', Balance '%s'",
$topup->{'ID'},
$topup->{'Value'}
);
# Topup depleted
} elsif ($topup->{'Value'} <= $uptimeOverUsage) {
push(@depletedTopups, $topup->{'ID'});
# Excess uptime remaining
$uptimeOverUsage -= $topup->{'Value'};
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME TOPUP DEPLETED: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$topup->{'ID'},
$topup->{'Value'},
$uptimeOverUsage
);
# Topup still alive
} else {
my $uptimeRemaining = $topup->{'Value'} - $uptimeOverUsage;
if ($topup->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $topup->{'ID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $uptimeRemaining
});
}
# All excess uptime has been "paid" for
$uptimeOverUsage = 0;
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME TOPUP USAGE: ".
"TOPUPID '%s', Balance '%s', Overage Left '%s'",
$topup->{'ID'},
$uptimeRemaining,
$uptimeOverUsage
);
}
}
......@@ -763,9 +969,15 @@ sub cleanup
if ($summaryItem->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $summaryItem->{'TopupID'},
PeriodKey => $periodKey,
Balance => $summaryItem->{'Value'}
PeriodKey => $curPeriodKey,
Balance => $summaryItem->{'Balance'}
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME SUMMARY CARRY: ".
"TOPUPID '%s', Balance '%s'",
$summaryItem->{'TopupID'},
$summaryItem->{'Balance'}
);
}
}
# Check for topups
......@@ -774,29 +986,42 @@ sub cleanup
if ($topup->{'ValidTo'} >= $unix_nextMonth) {
push(@summaryTopups, {
ID => $topup->{'ID'},
PeriodKey => $periodKey,
PeriodKey => $curPeriodKey,
Balance => $topup->{'Value'}
});
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => UPTIME TOPUP CARRY: ".
"TOPUPID '%s', Balance '%s'",
$topup->{'ID'},
$topup->{'Value'}
);
}
}
}
# Loop through summary topups
foreach my $summaryTopup (@summaryTopups) {
# Set users depleted topups
# Create topup summaries
$sth = DBDo('
INSERT INTO
@TP@topups_summary (TopupID,PeriodKey,Balance)
VALUES
(?,?,?)
',
$summaryTopup->{'ID'},$periodKey,$summaryTopup->{'Balance'}
$summaryTopup->{'ID'},$curPeriodKey,$summaryTopup->{'Balance'}
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to update topups_summary: ".
awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to create topup summary: ".
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => CREATE TOPUP SUMMARY: ".
"TOPUPID '%s', Balance '%s'",
$summaryTopup->{'ID'},
$summaryTopup->{'Balance'}
);
}
# Loop through topups that are depleted
......@@ -806,17 +1031,23 @@ sub cleanup
UPDATE
@TP@topups
SET
Depleted = 1
Depleted = 1,
SMAdminDepletedOn = ?
WHERE
ID = ?
',
$topupID
$depletedTimestamp,$topupID
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to update topups: ".
awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to deplete topup: ".
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => DEPLETED TOPUP: ".
"TOPUPID '%s'",
$topupID
);
}
# Loop through topup summary items that are depleted
......@@ -826,22 +1057,28 @@ sub cleanup
UPDATE
@TP@topups_summary
SET
Depleted = 1
Depleted = 1,
SMAdminDepletedOn = ?
WHERE
TopupID = ?
',
$topupID
$depletedTimestamp,$topupID
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Failed to update topups_summary: ".
awitpt::db::dblayer::Error());
AWITPT::DB::DBLayer::error());
goto FAIL_ROLLBACK;
}
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => DEPLETED TOPUP SUMMARY: ".
"TOPUPID '%s'",
$topupID
);
}
}
# Finished
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Topup summaries have been updated");
$server->log(LOG_NOTICE,"[MOD_CONFIG_SQL_TOPUPS] Cleanup => Topups have been updated and summaries created");
DBCommit();
return;
......@@ -852,5 +1089,81 @@ FAIL_ROLLBACK:
}
## @addTopup
# Create a topup
#
# @param server Server object
# @param user User
# @param packet Radius packet
#
# @return Result
sub addTopup
{
my ($server,$user,$validFrom,$validTo,$type,$value) = @_;
# Build template
my $template;
$template->{'user'}->{'ID'} = $user->{'ID'};
$template->{'user'}->{'Username'} = $user->{'Username'};
my $now = DateTime->now->set_time_zone($server->{'smradius'}->{'event_timezone'});
$template->{'query'}->{'Timestamp'} = $now->strftime('%F %T');
$template->{'query'}->{'ValidFrom'} = $validFrom;
$template->{'query'}->{'ValidTo'} = $validTo;
$template->{'query'}->{'Type'} = $type;
$template->{'query'}->{'Value'} = $value;
$template->{'query'}->{'Depleted'} = 0;
# Replace template entries
my @dbDoParams = templateReplace($config->{'topups_add_query'},$template);
# Insert into database
my $sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_CONFIG_SQL_TOPUPS] Failed to insert topup record: %s",AWITPT::DB::DBLayer::error());
return MOD_RES_NACK;
}
return MOD_RES_ACK;
}
## @internal
# Function snippet to add up traffic summaries based on topup types
sub _trafficSummaryAdd
{
my ($trafficSummaries,$topup,$key) = @_;
# First we add up NON-autotopup's
if (!_isAutoTopup($topup->{'Type'})) {
# Add the topup amount to the appropriate hash entry
if (_isTrafficTopup($topup->{'Type'})) {
$trafficSummaries->{'traffic'} += $topup->{$key};
} elsif (_isUptimeTopup($topup->{'Type'})) {
$trafficSummaries->{'uptime'} += $topup->{$key};
}
# Next we add up auto-topups
} else {
if (_isTrafficTopup($topup->{'Type'})) {
# Add to traffic summary list
$trafficSummaries->{'traffic-auto'} += $topup->{$key};
} elsif (_isUptimeTopup($topup->{'Type'})) {
# Add to uptime summary list
$trafficSummaries->{'uptime-auto'} += $topup->{$key};
}
}
return;
}
1;
# vim: ts=4
# Test user database
# Copyright (C) 2007-2009, AllWorldIT
# Copyright (C) 2007-2011, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -15,7 +15,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::smradius::modules::mod_config_test;
package smradius::modules::system::mod_config_test;
use strict;
use warnings;
......
# SQL user database support
# Copyright (C) 2007-2009, AllWorldIT
#
# SQL user database support for mac authentication
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::userdb::mod_userdb_sql;
package smradius::modules::userdb::mod_userdb_macauth_sql;
use strict;
use warnings;
# Modules we need
use AWITPT::Cache;
use AWITPT::DB::DBLayer;
use AWITPT::Util;
use smradius::attributes;
use smradius::constants;
use smradius::logging;
use awitpt::db::dblayer;
use smradius::util;
use smradius::attributes;
# Exporter stuff
require Exporter;
......@@ -40,7 +42,7 @@ our (@ISA,@EXPORT,@EXPORT_OK);
# Plugin info
our $pluginInfo = {
Name => "SQL User Database",
Name => "SQL User Database (MAC authentication)",
Init => \&init,
# User database
......@@ -61,120 +63,120 @@ sub init
# Enable support for database
if (!$server->{'smradius'}->{'database'}->{'enabled'}) {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Enabling database support.");
$server->log(LOG_NOTICE,"[MOD_USERDB_MACAUTH_SQL] Enabling database support.");
$server->{'smradius'}->{'database'}->{'enabled'} = 1;
}
# Default configs...
$config->{'userdb_find_query'} = '
SELECT
ID
FROM
@TP@users
WHERE
Username = %{request.User-Name}
';
$config->{'userdb_get_group_attributes_query'} = '
SELECT
@TP@group_attributes.Name, @TP@group_attributes.Operator, @TP@group_attributes.Value
FROM
@TP@group_attributes, @TP@users_to_groups
WHERE
@TP@users_to_groups.UserID = %{userdb.id}
AND @TP@group_attributes.GroupID = @TP@users_to_groups.GroupID
';
$config->{'userdb_get_user_attributes_query'} = '
$config->{'userdb_macauth_find_query'} = '
SELECT
Name, Operator, Value
user_attributes.ID,
user_attributes.Operator, user_attributes.Disabled,
users.Username, users.Disabled AS UserDisabled
FROM
@TP@user_attributes
@TP@user_attributes, @TP@users
WHERE
UserID = %{userdb.ID}
user_attributes.Name = "Calling-Station-Id"
AND user_attributes.Value = %{user.MACAddress}
AND users.ID = user_attributes.UserID
';
# Setup SQL queries
if (defined($scfg->{'mod_userdb_sql'})) {
if (defined($scfg->{'mod_userdb_macauth_sql'})) {
# Pull in queries
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_find_query'}) &&
$scfg->{'mod_userdb_sql'}->{'userdb_find_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'userdb_find_query'}) eq "ARRAY") {
$config->{'userdb_find_query'} = join(' ', @{$scfg->{'mod_userdb_sql'}->{'userdb_find_query'}});
if (defined($scfg->{'mod_userdb_macauth_sql'}->{'userdb_macauth_find_query'}) &&
$scfg->{'mod_userdb_macauth_sql'}->{'userdb_macauth_find_query'} ne "") {
if (ref($scfg->{'mod_userdb_macauth_sql'}->{'userdb_macauth_find_query'}) eq "ARRAY") {
$config->{'userdb_macauth_find_query'} = join(' ', @{$scfg->{'mod_userdb_macauth_sql'}->{'userdb_macauth_find_query'}});
} else {
$config->{'userdb_find_query'} = $scfg->{'mod_userdb_sql'}->{'userdb_find_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'}) &&
$scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'}) eq "ARRAY") {
$config->{'userdb_get_group_attributes_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'}});
} else {
$config->{'userdb_get_group_attributes_query'} =
$scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'}) &&
$scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'}) eq "ARRAY") {
$config->{'userdb_get_user_attributes_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'}});
} else {
$config->{'userdb_get_user_attributes_query'} =
$scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'};
$config->{'userdb_macauth_find_query'} = $scfg->{'mod_userdb_macauth_sql'}->{'userdb_macauth_find_query'};
}
}
}
}
## @find
# Try find a user
#
# @param server Server object
# @param user User
# @param user SMRadius user hash
# @li Username Username of the user we want
# @param packet Radius packet
#
# @return Result
# @return _UserDB_Data Hash of db query, this is stored in the $user->{'_UserDB_Data'} hash item
sub find
{
my ($server,$user,$packet) = @_;
# Only valid for authentication
if ($packet->code ne "Access-Request") {
return MOD_RES_SKIP;
}
# Check if this could be a MAC auth attempt
if (!($user->{'Username'} =~ /^(?:[0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$/)) {
return MOD_RES_SKIP;
}
# Standardize the MAC address
my $macAddress;
($macAddress = $user->{'Username'}) =~ s/-/:/g;
$server->log(LOG_DEBUG,"[MOD_USERDB_MACAUTH_SQL] Possible MAC authentication '$macAddress'");
# Build template
my $template;
foreach my $attr ($packet->attributes) {
$template->{'request'}->{$attr} = $packet->rawattr($attr)
}
$template->{'user'} = $user;
# Add MAC address details
$template->{'user'}->{'MACAddress'} = $macAddress;
# Replace template entries
my @dbDoParams = templateReplace($config->{'userdb_find_query'},$template);
my @dbDoParams = templateReplace($config->{'userdb_macauth_find_query'},$template);
my $sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_USERDB_SQL] Failed to find user data: ".awitpt::db::dblayer::Error());
$server->log(LOG_ERR,"[MOD_USERDB_MACAUTH_SQL] Failed to find data for MAC address: ".AWITPT::DB::DBLayer::error());
return MOD_RES_SKIP;
}
# Check if we got a result, if we did not NACK
my $rows = $sth->rows();
if ($rows > 1) {
$server->log(LOG_ERR,"[MOD_USERDB_SQL] More than one result returned for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
} elsif ($rows < 1) {
$server->log(LOG_DEBUG,"[MOD_USERDB_SQL] User '".$user->{'Username'}."' not found in database");
if ($rows < 1) {
$server->log(LOG_DEBUG,"[MOD_USERDB_MACAUTH_SQL] MAC address '".$user->{'Username'}."' not found in database");
return MOD_RES_SKIP;
}
# Grab record data
my $row = $sth->fetchrow_hashref();
my $macDisabled;
while (my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID Operator Username Disabled UserDisabled))) {
# We only support ||= attributes
if ($row->{'Operator'} ne '||=') {
$server->log(LOG_DEBUG,"[MOD_USERDB_MACAUTH_SQL] MAC authentication only supports operator '||=', but '".
$row->{'Operator'}."' found for user '".$row->{'Username'}."'");
}
# Dont use disabled user
if (!defined($macDisabled)) {
$macDisabled = (isBoolean($row->{'Disabled'}) || isBoolean($row->{'UserDisabled'}));
} else {
# If MAC is disabled, just set ... worst case it can still be disabled
if ($macDisabled) {
$macDisabled = (!isBoolean($row->{'Disabled'}) && !isBoolean($row->{'UserDisabled'}));
}
}
}
if (defined($macDisabled) && $macDisabled) {
$server->log(LOG_DEBUG,"[MOD_USERDB_MACAUTH_SQL] MAC address '".$user->{'Username'}."' is disabled");
return MOD_RES_SKIP;
}
DBFreeRes($sth);
return (MOD_RES_ACK,$row);
return (MOD_RES_ACK,undef);
}
......@@ -182,10 +184,12 @@ sub find
# Try to get a user
#
# @param server Server object
# @param user User
# @param user Server $user hash
# @param packet Radius packet
#
# @return Result
# @return User attributes hash
# @li Attributes Radius attribute hash
# @li VAttributes Radius vendor attribute hash
sub get
{
my ($server,$user,$packet) = @_;
......@@ -197,47 +201,13 @@ sub get
}
# Add in userdb data
foreach my $item (keys %{$user->{'_UserDB_Data'}}) {
$template->{'userdb'}->{$item} = $user->{'_UserDB_Data'}->{$item};
$template->{'userdb'}->{$item} = $user->{'_UserDB_Data'}->{$item};
}
# Attributes to return
my %attributes = ();
my %vattributes = ();
# Replace template entries
my @dbDoParams = templateReplace($config->{'userdb_get_group_attributes_query'},$template);
# Query database
my $sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get group attributes: ".awitpt::db::dblayer::Error());
return -1;
}
# Loop with group attributes
while (my $row = $sth->fetchrow_hashref()) {
addAttribute($server,\%attributes,\%vattributes,hashifyLCtoMC($row,qw(Name Operator Value)));
}
DBFreeRes($sth);
# Replace template entries again
@dbDoParams = templateReplace($config->{'userdb_get_user_attributes_query'},$template);
# Query database
$sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get user attributes: ".awitpt::db::dblayer::Error());
return -1;
}
# Loop with group attributes
while (my $row = $sth->fetchrow_hashref()) {
addAttribute($server,\%attributes,\%vattributes,hashifyLCtoMC($row,qw(Name Operator Value)));
}
DBFreeRes($sth);
my $ret;
$ret->{'Attributes'} = \%attributes;
......
# SQL user database support
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::userdb::mod_userdb_sql;
use strict;
use warnings;
# Modules we need
use AWITPT::Cache;
use AWITPT::DB::DBLayer;
use AWITPT::Util;
use smradius::attributes;
use smradius::constants;
use smradius::logging;
use smradius::util;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
);
@EXPORT_OK = qw(
);
# Plugin info
our $pluginInfo = {
Name => "SQL User Database",
Init => \&init,
# User database
User_find => \&find,
User_get => \&get,
# Users data
Users_data_set => \&data_set,
Users_data_get => \&data_get,
# Cleanup run by smadmin
CleanupOrder => 95,
Cleanup => \&cleanup
};
# Module config
my $config;
## @internal
# Initialize module
sub init
{
my $server = shift;
my $scfg = $server->{'inifile'};
# Enable support for database
if (!$server->{'smradius'}->{'database'}->{'enabled'}) {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Enabling database support.");
$server->{'smradius'}->{'database'}->{'enabled'} = 1;
}
# Default configs...
$config->{'userdb_find_query'} = '
SELECT
ID, Disabled
FROM
@TP@users
WHERE
Username = %{user.Username}
';
$config->{'userdb_get_group_attributes_query'} = '
SELECT
@TP@group_attributes.Name, @TP@group_attributes.Operator, @TP@group_attributes.Value
FROM
@TP@group_attributes, @TP@users_to_groups
WHERE
@TP@users_to_groups.UserID = %{user.ID}
AND @TP@group_attributes.GroupID = @TP@users_to_groups.GroupID
AND @TP@group_attributes.Disabled = 0
';
$config->{'userdb_get_user_attributes_query'} = '
SELECT
Name, Operator, Value
FROM
@TP@user_attributes
WHERE
UserID = %{user.ID}
AND Disabled = 0
';
$config->{'users_data_set_query'} = '
INSERT INTO
@TP@users_data (UserID, LastUpdated, Name, Value)
VALUES
(
%{user.ID},
%{query.LastUpdated},
%{query.Name},
%{query.Value}
)
';
$config->{'users_data_update_query'} = '
UPDATE
@TP@users_data
SET
LastUpdated = %{query.LastUpdated},
Value = %{query.Value}
WHERE
UserID = %{user.ID}
AND Name = %{query.Name}
';
$config->{'users_data_get_query'} = '
SELECT
LastUpdated, Name, Value
FROM
@TP@users_data
WHERE
UserID = %{user.ID}
AND Name = %{query.Name}
';
$config->{'users_data_delete_query'} = '
DELETE FROM
@TP@users_data
WHERE
UserID = %{user.ID}
AND Name = %{query.Name}
';
# Default cache time for user data
$config->{'userdb_data_cache_time'} = 300;
# Setup SQL queries
if (defined($scfg->{'mod_userdb_sql'})) {
# Pull in queries
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_find_query'}) &&
$scfg->{'mod_userdb_sql'}->{'userdb_find_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'userdb_find_query'}) eq "ARRAY") {
$config->{'userdb_find_query'} = join(' ', @{$scfg->{'mod_userdb_sql'}->{'userdb_find_query'}});
} else {
$config->{'userdb_find_query'} = $scfg->{'mod_userdb_sql'}->{'userdb_find_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'}) &&
$scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'}) eq "ARRAY") {
$config->{'userdb_get_group_attributes_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'}});
} else {
$config->{'userdb_get_group_attributes_query'} =
$scfg->{'mod_userdb_sql'}->{'userdb_get_group_attributes_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'}) &&
$scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'}) eq "ARRAY") {
$config->{'userdb_get_user_attributes_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'}});
} else {
$config->{'userdb_get_user_attributes_query'} =
$scfg->{'mod_userdb_sql'}->{'userdb_get_user_attributes_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'users_data_set_query'}) &&
$scfg->{'mod_userdb_sql'}->{'users_data_set_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'users_data_set_query'}) eq "ARRAY") {
$config->{'users_data_set_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'users_data_set_query'}});
} else {
$config->{'users_data_set_query'} = $scfg->{'mod_userdb_sql'}->{'users_data_set_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'users_data_update_query'}) &&
$scfg->{'mod_userdb_sql'}->{'users_data_update_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'users_data_update_query'}) eq "ARRAY") {
$config->{'users_data_update_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'users_data_update_query'}});
} else {
$config->{'users_data_update_query'} = $scfg->{'mod_userdb_sql'}->{'users_data_update_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'users_data_get_query'}) &&
$scfg->{'mod_userdb_sql'}->{'users_data_get_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'users_data_get_query'}) eq "ARRAY") {
$config->{'users_data_get_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'users_data_get_query'}});
} else {
$config->{'users_data_get_query'} = $scfg->{'mod_userdb_sql'}->{'users_data_get_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'users_data_delete_query'}) &&
$scfg->{'mod_userdb_sql'}->{'users_data_delete_query'} ne "") {
if (ref($scfg->{'mod_userdb_sql'}->{'users_data_delete_query'}) eq "ARRAY") {
$config->{'users_data_delete_query'} = join(' ',
@{$scfg->{'mod_userdb_sql'}->{'users_data_delete_query'}});
} else {
$config->{'users_data_delete_query'} = $scfg->{'mod_userdb_sql'}->{'users_data_delete_query'};
}
}
if (defined($scfg->{'mod_userdb_sql'}->{'userdb_data_cache_time'})) {
if (defined(my $val = isBoolean($scfg->{'mod_userdb_sql'}{'userdb_data_cache_time'}))) {
# If val is true, we default to the default anyway
# We're disabled
if (!$val) {
$config->{'userdb_data_cache_time'} = undef;
}
# We *could* have a value...
} elsif ($scfg->{'mod_userdb_sql'}{'userdb_data_cache_time'} =~ /^[0-9]+$/) {
$config->{'userdb_data_cache_time'} = $scfg->{'mod_userdb_sql'}{'userdb_data_cache_time'};
} else {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Value for 'userdb_data_cache_time' is invalid");
}
}
}
# Log this for info sake
if (defined($config->{'userdb_data_cache_time'})) {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Users data caching ENABLED, cache time is %ds.",
$config->{'userdb_data_cache_time'});
} else {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Users caching DISABLED");
}
}
## @find
# Try find a user
#
# @param server Server object
# @param user SMRadius user hash
# @li Username Username of the user we want
# @param packet Radius packet
#
# @return _UserDB_Data Hash of db query, this is stored in the $user->{'_UserDB_Data'} hash item
sub find
{
my ($server,$user,$packet) = @_;
# Build template
my $template;
foreach my $attr ($packet->attributes) {
$template->{'request'}->{$attr} = $packet->rawattr($attr)
}
# Add user details, not user ID is available here as thats what we are retrieving
$template->{'user'}->{'Username'} = $user->{'Username'};
# Replace template entries
my @dbDoParams = templateReplace($config->{'userdb_find_query'},$template);
my $sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_USERDB_SQL] Failed to find user data: ".AWITPT::DB::DBLayer::error());
return MOD_RES_SKIP;
}
# Check if we got a result, if we did not NACK
my $rows = $sth->rows();
if ($rows > 1) {
$server->log(LOG_ERR,"[MOD_USERDB_SQL] More than one result returned for user '".$user->{'Username'}."'");
return MOD_RES_SKIP;
} elsif ($rows < 1) {
$server->log(LOG_DEBUG,"[MOD_USERDB_SQL] User '".$user->{'Username'}."' not found in database");
return MOD_RES_SKIP;
}
# Grab record data
my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(ID Disabled));
# Dont use disabled user
my $res = isBoolean($row->{'Disabled'});
if ($res) {
$server->log(LOG_DEBUG,"[MOD_USERDB_SQL] User '".$user->{'Username'}."' is disabled");
return MOD_RES_SKIP;
}
DBFreeRes($sth);
return (MOD_RES_ACK,$row);
}
## @get
# Try to get a user
#
# @param server Server object
# @param user Server $user hash
# @param packet Radius packet
#
# @return User attributes hash
# @li Attributes Radius attribute hash
# @li VAttributes Radius vendor attribute hash
sub get
{
my ($server,$user,$packet) = @_;
# Build template
my $template;
foreach my $attr ($packet->attributes) {
$template->{'request'}->{$attr} = $packet->rawattr($attr)
}
# Add user details
$template->{'user'}->{'ID'} = $user->{'ID'};
$template->{'user'}->{'Username'} = $user->{'Username'};
# Add in userdb data
foreach my $item (keys %{$user->{'_UserDB_Data'}}) {
$template->{'userdb'}->{$item} = $user->{'_UserDB_Data'}->{$item};
}
# Replace template entries
my @dbDoParams = templateReplace($config->{'userdb_get_group_attributes_query'},$template);
# Query database
my $sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get group attributes: ".AWITPT::DB::DBLayer::error());
return RES_ERROR;
}
# Loop with group attributes
while (my $row = $sth->fetchrow_hashref()) {
addAttribute($server,$user,hashifyLCtoMC($row,qw(Name Operator Value)));
}
DBFreeRes($sth);
# Replace template entries again
@dbDoParams = templateReplace($config->{'userdb_get_user_attributes_query'},$template);
# Query database
$sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get user attributes: ".AWITPT::DB::DBLayer::error());
return RES_ERROR;
}
# Loop with user attributes
while (my $row = $sth->fetchrow_hashref()) {
addAttribute($server,$user,hashifyLCtoMC($row,qw(Name Operator Value)));
}
DBFreeRes($sth);
return RES_OK;
}
## @data_set
# Set user data
#
# @param server Server object
# @param user Server $user hash
# @param module Module that is variable pertains to
# @param name Variable name
# @param value Variable value
#
# @return RES_OK on success, RES_ERROR on error
sub data_set
{
my ($server, $user, $module, $name, $value) = @_;
# Build template
my $template;
# Add user details
$template->{'user'}->{'ID'} = $user->{'ID'};
$template->{'user'}->{'Username'} = $user->{'Username'};
# Add in userdb data
foreach my $item (keys %{$user->{'_UserDB_Data'}}) {
$template->{'userdb'}->{$item} = $user->{'_UserDB_Data'}->{$item};
}
# Last updated time would be now
$template->{'query'}->{'LastUpdated'} = $user->{'_Internal'}->{'Timestamp'};
$template->{'query'}->{'Name'} = sprintf('%s/%s',$module,$name);
$template->{'query'}->{'Value'} = $value;
# Replace template entries
my @dbDoParams = templateReplace($config->{'users_data_update_query'},$template);
# Query database
my $sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to update users data: ".AWITPT::DB::DBLayer::error());
return RES_ERROR;
}
# If we updated *something* ...
if ($sth eq "0E0") {
@dbDoParams = templateReplace($config->{'users_data_set_query'},$template);
# Insert
$sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to set users data: ".AWITPT::DB::DBLayer::error());
return RES_ERROR;
}
}
# If we using caching, cache the result of this set
if (defined($config->{'userdb_data_cache_time'})) {
# Build hash to store
my %data;
$data{'CachedUntil'} = $user->{'_Internal'}->{'Timestamp-Unix'} + $config->{'userdb_data_cache_time'};
$data{'LastUpdated'} = $user->{'_Internal'}->{'Timestamp'};
$data{'Module'} = $module;
$data{'Name'} = $name;
$data{'Value'} = $value;
# Cache the result
cacheStoreComplexKeyPair('mod_userdb_sql(users_data)',
sprintf('%s/%s/%s',$module,$user->{'_UserDB_Data'}->{'ID'},$name),
\%data
);
}
return RES_OK;
}
## @data_get
# Get user data
#
# @param server Server object
# @param user UserDB hash we got from find()
# @param module Module that is variable pertains to
# @param name Variable name
#
# @return Users data hash
# @li LastUpdated Time of last update
# @li Name Variable Name
# @li Value Variable Value
sub data_get
{
my ($server, $user, $module, $name) = @_;
# Build template
my $template;
# Add user details
$template->{'user'}->{'ID'} = $user->{'ID'};
$template->{'user'}->{'Username'} = $user->{'Username'};
# Add in userdb data
foreach my $item (keys %{$user->{'_UserDB_Data'}}) {
$template->{'userdb'}->{$item} = $user->{'_UserDB_Data'}->{$item};
}
$template->{'query'}->{'Name'} = sprintf('%s/%s',$module,$name);
# If we using caching, check how old the result is
if (defined($config->{'userdb_data_cache_time'})) {
my ($res,$val) = cacheGetComplexKeyPair('mod_userdb_sql(data_get)',
sprintf('%s/%s/%s',$module,$user->{'_UserDB_Data'}->{'ID'},$name)
);
if (defined($val) && $val->{'CachedUntil'} > $user->{'_Internal'}->{'Timestamp-Unix'}) {
return $val;
}
}
# Replace template entries
my @dbDoParams = templateReplace($config->{'users_data_get_query'},$template);
# Query database
my $sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"Failed to get users data: ".AWITPT::DB::DBLayer::error());
return RES_ERROR;
}
# Fetch user data
my $row = hashifyLCtoMC($sth->fetchrow_hashref(), qw(LastUpdated Name Value));
# If there is no result, just return undef
return if (!defined($row));
# If there is data, go through the long process of continuing ...
my %data;
$data{'LastUpdated'} = $row->{'LastUpdated'};
$data{'Module'} = $module;
$data{'Name'} = $row->{'Name'};
$data{'Value'} = $row->{'Value'};
# If we using caching and got here, it means that we must cache the result
if (defined($config->{'userdb_data_cache_time'})) {
$data{'CachedUntil'} = $user->{'_Internal'}->{'Timestamp-Unix'} + $config->{'userdb_data_cache_time'};
# Cache the result
cacheStoreComplexKeyPair('mod_userdb_sql(users_data)',
sprintf('%s/%s/%s',$module,$user->{'_UserDB_Data'}->{'ID'},$name),
\%data
);
}
return \%data;
}
# Clean up of old user variables
sub cleanup
{
my ($server,$runForDate,$resetUserData) = @_;
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Cleanup => Removing old user data");
# Begin operation
DBBegin();
# Perform query
my $sth = DBDo('
DELETE FROM
@TP@users_data
WHERE UserID NOT IN
(
SELECT ID FROM users
)
');
# Error and rollback
if (!$sth) {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Cleanup => Database has been rolled back, no data deleted");
DBRollback();
return;
}
if ($resetUserData) {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Cleanup => Resetting user data counters");
# Perform query
my $sth = DBDo('
UPDATE
@TP@users_data
SET
Value = 0
WHERE
Name = '.DBQuote('CurrentMonthTotalTraffic').'
OR Name = '.DBQuote('CurrentMonthTotalUptime').'
');
# Error and rollback
if (!$sth) {
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Cleanup => Database has been rolled back, no data reset");
DBRollback();
return;
}
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Cleanup => User data counters have been reset");
}
# Commit
DBCommit();
$server->log(LOG_NOTICE,"[MOD_USERDB_SQL] Cleanup => Old user data cleaned up");
}
1;
# vim: ts=4
# Test user database
# Copyright (C) 2007-2009, AllWorldIT
# Copyright (C) 2007-2011, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......
# SMRadius Utility Functions
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
=encoding utf8
=head1 NAME
smradius::util - SMRadius utils
=head1 SYNOPSIS
my ($str,@vals) = templateReplace("SELECT * FROM abc WHERE %{abc} = ?",{ 'abc' => "some value" });
my $str = quickTemplateToolkit('someval is "[% someval %]"',{ 'someval' = "hello world" });
=head1 DESCRIPTION
The smradius::util class provides utility classes for SMRadius.
=cut
package smradius::util;
use parent qw(Exporter);
use strict;
use warnings;
our (@EXPORT_OK,@EXPORT);
@EXPORT_OK = qw(
);
@EXPORT = qw(
templateReplace
quickTemplateToolkit
);
use Template;
=head1 METHODS
The following utility methods are available.
=cut
=head2 templateReplace
my ($str,@vals) = templateReplace("SELECT * FROM abc WHERE %{abc} = ?",{ 'abc' => "some value" });
The C<templatereplace> method is used to replace variables with a placeholder. This is very useful for SQL templates. The values
are returned in the second and subsequent array items.
=over
=back
=cut
# Replace hashed variables with placeholders and return an array with the values.
sub templateReplace
{
my ($string,$hashref,$placeholder) = @_;
my @valueArray = ();
$placeholder //= '?';
# Replace blanks
while (my ($entireMacro,$section,$item,$default) = ($string =~ /(\%\{([a-z]+)\.([a-z0-9\-]+)(?:=([^\}]*))?\})/i )) {
# Replace macro with ? or the placeholder if specified
# We also quote the entireMacro
$string =~ s/\Q$entireMacro\E/$placeholder/;
# Get value to substitute
my $value = (defined($hashref->{$section}) && defined($hashref->{$section}->{$item})) ?
$hashref->{$section}->{$item} : $default;
# Add value onto our array
push(@valueArray,$value);
}
return ($string, @valueArray);
}
=head2 quickTemplateToolkit
my $str = quickTemplateToolkit('someval is "[% someval %]"',{ 'someval' = "hello world" });
The C<quickTemplateToolkit> is a quick and easy template toolkit function.
=over
=back
=cut
# Replace hashed variables with placeholders and return an array with the values.
sub quickTemplateToolkit
{
my ($string,$variables) = @_;
# This is the config we're going to pass to Template
my $config = {
# Our include path built below
INCLUDE_PATH => [ ],
};
# Create template engine
my $tt = Template->new($config);
# Process the template and output to our OUTPUT_PATH
my $output = "";
if (!(my $res = $tt->process(\$string, $variables, \$output))) {
return (undef,$tt->error());
}
return $output;
}
1;
__END__
=head1 AUTHORS
Nigel Kukard E<lt>nkukard@lbsd.netE<gt>
=head1 BUGS
All bugs should be reported via the project issue tracker
L<http://gitlab.devlabs.linuxassist.net/awit-frameworks/awit-perl-toolkit/issues/>.
=head1 LICENSE AND COPYRIGHT
Copyright (C) 2007-2016, AllWorldIT
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
=head1 SEE ALSO
L<Template>.
=cut
# AWRadius version package
# Copyright (C) 2007-2009, AllWorldIT
#
# SMRadius version package
# Copyright (C) 2007-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
......@@ -27,13 +27,19 @@ require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
$VERSION
VERSION
);
@EXPORT_OK = qw(
);
our $VERSION = "1.0.1";
use constant {
VERSION => "0.0.1",
};
sub VERSION { return $VERSION };
......
# SQL accounting database
# Copyright (C) 2007-2009, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::accounting::mod_accounting_sql;
use strict;
use warnings;
# Modules we need
use smradius::constants;
use awitpt::db::dblayer;
use smradius::logging;
use smradius::util;
use POSIX qw(ceil);
use DateTime;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
);
@EXPORT_OK = qw(
);
# Plugin info
our $pluginInfo = {
Name => "SQL Accounting Database",
Init => \&init,
# Cleanup run by smadmin
Cleanup => \&cleanup,
# Accounting database
Accounting_log => \&acct_log,
Accounting_getUsage => \&getUsage
};
# Module config
my $config;
## @internal
# Initialize module
sub init
{
my $server = shift;
my $scfg = $server->{'inifile'};
# Enable support for database
$server->log(LOG_NOTICE,"[MOD_ACCOUNTING_SQL] Enabling database support");
if (!$server->{'smradius'}->{'database'}->{'enabled'}) {
$server->log(LOG_NOTICE,"[MOD_ACCOUNTING_SQL] Enabling database support.");
$server->{'smradius'}->{'database'}->{'enabled'} = 1;
}
# Default configs...
$config->{'accounting_start_query'} = '
INSERT INTO
@TP@accounting
(
Username,
ServiceType,
FramedProtocol,
NASPort,
NASPortType,
CallingStationID,
CalledStationID,
NASPortID,
AcctSessionID,
FramedIPAddress,
AcctAuthentic,
EventTimestamp,
AcctStatusType,
NASIdentifier,
NASIPAddress,
AcctDelayTime
)
VALUES
(
%{request.User-Name},
%{request.Service-Type},
%{request.Framed-Protocol},
%{request.NAS-Port},
%{request.NAS-Port-Type},
%{request.Calling-Station-Id},
%{request.Called-Station-Id},
%{request.NAS-Port-Id},
%{request.Acct-Session-Id},
%{request.Framed-IP-Address},
%{request.Acct-Authentic},
%{request.Timestamp},
%{request.Acct-Status-Type},
%{request.NAS-Identifier},
%{request.NAS-IP-Address},
%{request.Acct-Delay-Time}
)
';
$config->{'accounting_update_query'} = '
UPDATE
@TP@accounting
SET
AcctSessionTime = %{request.Acct-Session-Time},
AcctInputOctets = %{request.Acct-Input-Octets},
AcctInputGigawords = %{request.Acct-Input-Gigawords},
AcctInputPackets = %{request.Acct-Input-Packets},
AcctOutputOctets = %{request.Acct-Output-Octets},
AcctOutputGigawords = %{request.Acct-Output-Gigawords},
AcctOutputPackets = %{request.Acct-Output-Packets},
AcctStatusType = %{request.Acct-Status-Type}
WHERE
Username = %{request.User-Name}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
';
$config->{'accounting_stop_query'} = '
UPDATE
@TP@accounting
SET
AcctSessionTime = %{request.Acct-Session-Time},
AcctInputOctets = %{request.Acct-Input-Octets},
AcctInputGigawords = %{request.Acct-Input-Gigawords},
AcctInputPackets = %{request.Acct-Input-Packets},
AcctOutputOctets = %{request.Acct-Output-Octets},
AcctOutputGigawords = %{request.Acct-Output-Gigawords},
AcctOutputPackets = %{request.Acct-Output-Packets},
AcctStatusType = %{request.Acct-Status-Type},
AcctTerminateCause = %{request.Acct-Terminate-Cause}
WHERE
Username = %{request.User-Name}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
';
$config->{'accounting_usage_query'} = '
SELECT
SUM(AcctInputOctets) AS InputOctets,
SUM(AcctOutputOctets) AS OutputOctets,
SUM(AcctInputGigawords) AS InputGigawords,
SUM(AcctOutputGigawords) AS OutputGigawords,
SUM(AcctSessionTime) AS SessionTime
FROM
@TP@accounting
WHERE
Username = %{request.User-Name}
';
# Setup SQL queries
if (defined($scfg->{'mod_accounting_sql'})) {
# Pull in queries
if (defined($scfg->{'mod_accounting_sql'}->{'accounting_start_query'}) &&
$scfg->{'mod_accounting_sql'}->{'accounting_start_query'} ne "") {
if (ref($scfg->{'mod_accounting_sql'}->{'accounting_start_query'}) eq "ARRAY") {
$config->{'accounting_start_query'} = join(' ',
@{$scfg->{'mod_accounting_sql'}->{'accounting_start_query'}});
} else {
$config->{'accounting_start_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_start_query'};
}
}
if (defined($scfg->{'mod_accounting_sql'}->{'accounting_update_query'}) &&
$scfg->{'mod_accounting_sql'}->{'accounting_update_query'} ne "") {
if (ref($scfg->{'mod_accounting_sql'}->{'accounting_update_query'}) eq "ARRAY") {
$config->{'accounting_update_query'} = join(' ',
@{$scfg->{'mod_accounting_sql'}->{'accounting_update_query'}});
} else {
$config->{'accounting_update_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_update_query'};
}
}
if (defined($scfg->{'mod_accounting_sql'}->{'accounting_stop_query'}) &&
$scfg->{'mod_accounting_sql'}->{'accounting_stop_query'} ne "") {
if (ref($scfg->{'mod_accounting_sql'}->{'accounting_stop_query'}) eq "ARRAY") {
$config->{'accounting_stop_query'} = join(' ',
@{$scfg->{'mod_accounting_sql'}->{'accounting_stop_query'}});
} else {
$config->{'accounting_stop_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_stop_query'};
}
}
if (defined($scfg->{'mod_accounting_sql'}->{'accounting_usage_query'}) &&
$scfg->{'mod_accounting_sql'}->{'accounting_usage_query'} ne "") {
if (ref($scfg->{'mod_accounting_sql'}->{'accounting_usage_query'}) eq "ARRAY") {
$config->{'accounting_usage_query'} = join(' ',
@{$scfg->{'mod_accounting_sql'}->{'accounting_usage_query'}});
} else {
$config->{'accounting_usage_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_usage_query'};
}
}
}
}
# Function to get radius user data usage
sub getUsage
{
my ($server,$user,$packet) = @_;
# Build template
my $template;
foreach my $attr ($packet->attributes) {
$template->{'request'}->{$attr} = $packet->rawattr($attr)
}
$template->{'user'} = $user;
# Replace template entries
my @dbDoParams = templateReplace($config->{'accounting_usage_query'},$template);
# Fetch data
my $sth = DBSelect(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Database query failed: ".awitpt::db::dblayer::Error());
return;
}
# Check rows
if ($sth->rows != 1) {
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Database: No accounting data returned for user");
return;
}
# Pull data
my $usageData = hashifyLCtoMC(
$sth->fetchrow_hashref(),
qw(InputOctets OutputOctets InputGigawords OutputGigawords SessionTime)
);
DBFreeRes($sth);
# FIXME, as its a custom query, check we have all the fields we need
# Total up input
my $totalData = 0;
if (defined($usageData->{'InputOctets'}) && $usageData->{'InputOctets'} > 0) {
$totalData += $usageData->{'InputOctets'} / 1024 / 1024;
}
if (defined($usageData->{'InputGigawords'}) && $usageData->{'InputGigawords'} > 0) {
$totalData += $usageData->{'InputGigawords'} * 4096;
}
# Add up output
if (defined($usageData->{'OutputOctets'}) && $usageData->{'OutputOctets'} > 0) {
$totalData += $usageData->{'OutputOctets'} / 1024 / 1024;
}
if (defined($usageData->{'OutputGigawords'}) && $usageData->{'OutputGigawords'} > 0) {
$totalData += $usageData->{'OutputGigawords'} * 4096;
}
# Add up time
my $totalTime = 0;
if (defined($usageData->{'SessionTime'}) && $usageData->{'SessionTime'} > 0) {
$totalTime = $usageData->{'SessionTime'} / 60;
}
# Rounding up
my %res;
$res{'TotalDataUsage'} = ceil($totalData);
$res{'TotalTimeUsage'} = ceil($totalTime);
return \%res;
}
## @log
# Try find a user
#
# @param server Server object
# @param user User object
# @param packet Radius packet
#
# @return Result
sub acct_log
{
my ($server,$user,$packet) = @_;
# Build template
my $template;
foreach my $attr ($packet->attributes) {
$template->{'request'}->{$attr} = $packet->rawattr($attr)
}
# Fix event timestamp
$template->{'request'}->{'Timestamp'} = $user->{'_Internal'}->{'Timestamp'};
# Add user
$template->{'user'} = $user;
if ($packet->attr('Acct-Status-Type') eq "Start") {
# Replace template entries
my @dbDoParams = templateReplace($config->{'accounting_start_query'},$template);
# Insert into database
my $sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Failed to insert accounting START record: ".
awitpt::db::dblayer::Error());
return MOD_RES_NACK;
}
} elsif ($packet->attr('Acct-Status-Type') eq "Alive") {
# Replace template entries
my @dbDoParams = templateReplace($config->{'accounting_update_query'},$template);
# Update database
my $sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Failed to update accounting ALIVE record: ".
awitpt::db::dblayer::Error());
return MOD_RES_NACK;
}
} elsif ($packet->attr('Acct-Status-Type') eq "Stop") {
# Replace template entries
my @dbDoParams = templateReplace($config->{'accounting_stop_query'},$template);
# Update database
my $sth = DBDo(@dbDoParams);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Failed to update accounting STOP record: ".awitpt::db::dblayer::Error());
return MOD_RES_NACK;
}
}
return MOD_RES_ACK;
}
# Add up totals function
sub cleanup
{
my ($server) = @_;
my ($prevYear,$prevMonth);
# The datetime now..
my $now = DateTime->now;
# If this is a new year
if ($now->month == 1) {
$prevYear = $now->year - 1;
$prevMonth = 12;
} else {
$prevYear = $now->year;
$prevMonth = $now->month - 1;
}
# New datetime
my $lastMonth = DateTime->new( year => $prevYear, month => $prevMonth, day => 1 );
# Update totals for last month
my $sth = DBSelect('
SELECT
Username,
SUM(AcctSessionTime) as AcctSessionTime,
SUM(AcctInputOctets) as AcctInputOctets,
SUM(AcctInputGigawords) as AcctInputGigawords,
SUM(AcctOutputOctets) as AcctOutputOctets,
SUM(AcctOutputGigawords) as AcctOutputGigawords
FROM
@TP@accounting
WHERE
EventTimestamp > ?
GROUP BY
Username
',
$lastMonth->ymd
);
if (!$sth) {
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Cleanup => Failed to select accounting record: ".
awitpt::db::dblayer::Error());
return;
}
# Set blank array
my @allRecords = ();
my $i = 0;
# Load items into array
while (my $usageTotals = $sth->fetchrow_hashref()) {
$usageTotals = hashifyLCtoMC(
$usageTotals,
qw(Username AcctSessionTime AcctInputOctets AcctInputGigawords AcctOutputOctets AcctOutputGigawords)
);
# Set array blank
my @recordRow = ();
# Set array items
@recordRow = (
$usageTotals->{'Username'},
$lastMonth->year."-".$lastMonth->month,
$usageTotals->{'AcctSessionTime'},
$usageTotals->{'AcctInputOctets'},
$usageTotals->{'AcctInputGigawords'},
$usageTotals->{'AcctOutputOctets'},
$usageTotals->{'AcctOutputGigawords'}
);
# Add record ontp @allRecords
@{$allRecords[$i]} = @recordRow;
# Increate array size
$i++;
}
# Begin transaction
DBBegin();
my @dbDoParams = ();
my $count = length(@allRecords);
# Update totals for last month
for ($i = 0; $i < $count; $i++) {
@dbDoParams = ('
INSERT INTO
@TP@accounting_summary
(
Username,
PeriodKey,
AcctSessionTime,
AcctInputOctets,
AcctInputGigawords,
AcctOutputOctets,
AcctOutputGigawords
)
VALUES
(?,?,?,?,?,?,?)
',
@{$allRecords[$i]}
);
if ($sth) {
# Do query
$sth = DBDo(@dbDoParams);
}
}
# Rollback with error if failed
if (!$sth) {
DBRollback();
$server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Cleanup => Failed to insert accounting record: ".
awitpt::db::dblayer::Error());
return;
}
# Commit if succeeded
DBCommit();
$server->log(LOG_NOTICE,"[MOD_ACCOUNTING_SQL] Cleanup => Totals have been updated");
}
1;
# vim: ts=4
# Capping support
# Copyright (C) 2007-2009, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
package smradius::modules::features::mod_feature_capping;
use strict;
use warnings;
# Modules we need
use smradius::constants;
use smradius::logging;
use smradius::util;
use Date::Parse;
# Exporter stuff
require Exporter;
our (@ISA,@EXPORT,@EXPORT_OK);
@ISA = qw(Exporter);
@EXPORT = qw(
);
@EXPORT_OK = qw(
);
# Plugin info
our $pluginInfo = {
Name => "User Capping Feature",
Init => \&init,
# Authentication hook
'Feature_Post-Authentication_hook' => \&post_auth_hook,
# Accounting hook
'Feature_Post-Accounting_hook' => \&post_acct_hook,
};
# Some constants
my $TRAFFIC_LIMIT_KEY = 'SMRadius-Capping-Traffic-Limit';
my $UPTIME_LIMIT_KEY = 'SMRadius-Capping-Uptime-Limit';
my $TRAFFIC_TOPUPS_KEY = 'SMRadius-Capping-Traffic-Topup';
my $TIME_TOPUPS_KEY = 'SMRadius-Capping-Uptime-Topup';
## @internal
# Initialize module
sub init
{
my $server = shift;
}
## @post_auth_hook($server,$user,$packet)
# Post authentication hook
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub post_auth_hook
{
my ($server,$user,$packet) = @_;
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] POST AUTH HOOK");
my ($trafficLimit,$uptimeLimit);
# Get uptime limit
if (defined($user->{'Attributes'}->{$UPTIME_LIMIT_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$UPTIME_LIMIT_KEY."' is defined");
# Operator: :=
if (defined($user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='})) {
# Is it a number?
if ($user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) {
$uptimeLimit = $user->{'Attributes'}->{$UPTIME_LIMIT_KEY};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'}.
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".
$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}."'");
}
}
# Get traffic limit
if (defined($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_LIMIT_KEY."' is defined");
# Operator: +=
if (defined($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='})) {
# Is it a number?
if ($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) {
$trafficLimit = $user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'}.
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".
$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}."'");
}
}
# Get the users' usage
my $accountingUsage;
# Loop with plugins to find anyting supporting getting of usage
foreach my $module (@{$server->{'module_list'}}) {
# Do we have the correct plugin?
if ($module->{'Accounting_getUsage'}) {
$server->log(LOG_INFO,"[MOD_FEATURE_CAPPING] Found plugin: '".$module->{'Name'}."'");
# Fetch users session uptime & bandwidth used
my $res = $module->{'Accounting_getUsage'}($server,$user,$packet);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_FEATURE_CAPPING] No usage data found for user '".$packet->attr('User-Name')."'");
return MOD_RES_SKIP;
}
$accountingUsage = $res;
}
}
# Get topups
my $uptimeTopup = 0;
if (defined($user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TIME_TOPUPS_KEY."' is defined");
# Is there a value?
if (defined($user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0])) {
# Is it a number?
if ($user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0] =~ /^[0-9]+$/) {
$uptimeTopup = $user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0];
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0].
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$TIME_TOPUPS_KEY."' has no value");
}
}
# Set allowed uptime usage
my $alteredUptimeLimit = 0;
# Topups available
if ($uptimeTopup > 0) {
if (defined($uptimeLimit->{':='}->{'Value'})) {
$alteredUptimeLimit = $uptimeLimit->{':='}->{'Value'} + $uptimeTopup;
} else {
$alteredUptimeLimit = $uptimeTopup;
}
# No topups available
} else {
if (defined($uptimeLimit->{':='}->{'Value'})) {
$alteredUptimeLimit = $uptimeLimit->{':='}->{'Value'};
}
}
# Uptime usage
if (!(defined($uptimeLimit->{':='}->{'Value'}) && $uptimeLimit->{':='}->{'Value'} == 0)) {
if (!defined($uptimeLimit->{':='}->{'Value'})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Uptime => Usage total: ".$accountingUsage->{'TotalTimeUsage'}.
"Min (Cap: Prepaid, Topups: ".$uptimeTopup."Min)");
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Uptime => Usage total: ".$accountingUsage->{'TotalTimeUsage'}.
"Min (Cap: ".$uptimeLimit->{':='}->{'Value'}."Min, Topups: ".$uptimeTopup."Min)");
}
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Uptime => Usage total: ".$accountingUsage->{'TotalTimeUsage'}.
"Min (Cap: Uncapped, Topups: ".$uptimeTopup."Min)");
}
# If uptime limit exceeded, cap user
if (!(defined($uptimeLimit->{':='}->{'Value'}) && $uptimeLimit->{':='}->{'Value'} == 0)) {
if ($accountingUsage->{'TotalTimeUsage'} >= $alteredUptimeLimit) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage of ".$accountingUsage->{'TotalTimeUsage'}.
"Min exceeds allowed limit of ".$alteredUptimeLimit."Min. Capped.");
return MOD_RES_NACK;
}
}
# Get topups
my $trafficTopup = 0;
if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' is defined");
# Operator: +=
if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0])) {
# Is it a number?
if ($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0] =~ /^[0-9]+$/) {
$trafficTopup = $user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0];
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0].
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' has no value");
}
}
# Set allowed traffic usage
my $alteredTrafficLimit = 0;
# Topups available
if ($trafficTopup > 0) {
if (defined($trafficLimit->{':='}->{'Value'})) {
$alteredTrafficLimit = $trafficLimit->{':='}->{'Value'} + $trafficTopup;
} else {
$alteredTrafficLimit = $trafficTopup;
}
# No topups available
} else {
if (defined($trafficLimit->{':='}->{'Value'})) {
$alteredTrafficLimit = $trafficLimit->{':='}->{'Value'};
}
}
# Traffic usage
if (!(defined($trafficLimit->{':='}->{'Value'}) && $trafficLimit->{':='}->{'Value'} == 0)) {
if (!defined($trafficLimit->{':='}->{'Value'})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}.
"Mb (Cap: Prepaid, Topups: ".$trafficTopup."Mb)");
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}.
"Mb (Cap: ".$trafficLimit->{':='}->{'Value'}."Mb, Topups: ".$trafficTopup."Mb)");
}
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}.
"Mb (Cap: Uncapped, Topups: ".$trafficTopup."Mb)");
}
# If traffic limit exceeded, cap user
if (!(defined($trafficLimit->{':='}->{'Value'}) && $trafficLimit->{':='}->{'Value'} == 0)) {
if ($accountingUsage->{'TotalDataUsage'} >= $alteredTrafficLimit) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage of ".$accountingUsage->{'TotalDataUsage'}.
"Mb exceeds allowed limit of ".$alteredTrafficLimit."Mb. Capped.");
return MOD_RES_NACK;
}
}
return MOD_RES_ACK;
}
## @post_acct_hook($server,$user,$packet)
# Post authentication hook
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub post_acct_hook
{
my ($server,$user,$packet) = @_;
# Exceeding maximum, must be disconnected
return MOD_RES_SKIP if ($packet->attr('Acct-Status-Type') ne "Alive");
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] POST ACCT HOOK");
my ($trafficLimit,$uptimeLimit);
# Get uptime limit
if (defined($user->{'Attributes'}->{$UPTIME_LIMIT_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$UPTIME_LIMIT_KEY."' is defined");
# Operator: :=
if (defined($user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='})) {
# Is it a number?
if ($user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) {
$uptimeLimit = $user->{'Attributes'}->{$UPTIME_LIMIT_KEY};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}->{':='}->{'Value'}.
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".
$user->{'Attributes'}->{$UPTIME_LIMIT_KEY}."'");
}
}
# Get traffic limit
if (defined($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_LIMIT_KEY."' is defined");
# Operator: :=
if (defined($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='})) {
# Is it a number?
if ($user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'} =~ /^[0-9]+$/) {
$trafficLimit = $user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY};
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}->{':='}->{'Value'}.
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] No valid operators for attribute '".
$user->{'Attributes'}->{$TRAFFIC_LIMIT_KEY}."'");
}
}
# Get the users' usage
my $accountingUsage;
# Loop with plugins to find anyting supporting getting of usage
foreach my $module (@{$server->{'module_list'}}) {
# Do we have the correct plugin?
if ($module->{'Accounting_getUsage'}) {
$server->log(LOG_INFO,"[MOD_FEATURE_CAPPING] Found plugin: '".$module->{'Name'}."'");
# Fetch users session uptime & bandwidth used
my $res = $module->{'Accounting_getUsage'}($server,$user,$packet);
if (!defined($res)) {
$server->log(LOG_ERR,"[MOD_FEATURE_CAPPING] No usage data found for user '".$packet->attr('User-Name')."'");
return MOD_RES_SKIP;
}
$accountingUsage = $res;
}
}
# Get topups
my $uptimeTopup = 0;
if (defined($user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TIME_TOPUPS_KEY."' is defined");
# Is there a value?
if (defined($user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0])) {
# Is it a number?
if ($user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0] =~ /^[0-9]+$/) {
$uptimeTopup = $user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0];
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'ConfigAttributes'}->{$TIME_TOPUPS_KEY}->[0].
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$TIME_TOPUPS_KEY."' has no value");
}
}
# Set allowed uptime usage
my $alteredUptimeLimit = 0;
# Topups available
if ($uptimeTopup > 0) {
if (defined($uptimeLimit->{':='}->{'Value'})) {
$alteredUptimeLimit = $uptimeLimit->{':='}->{'Value'} + $uptimeTopup;
} else {
$alteredUptimeLimit = $uptimeTopup;
}
# No topups available
} else {
if (defined($uptimeLimit->{':='}->{'Value'})) {
$alteredUptimeLimit = $uptimeLimit->{':='}->{'Value'};
}
}
# Uptime usage
if (!(defined($uptimeLimit->{':='}->{'Value'}) && $uptimeLimit->{':='}->{'Value'} == 0)) {
if (!defined($uptimeLimit->{':='}->{'Value'})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Uptime => Usage total: ".$accountingUsage->{'TotalTimeUsage'}.
"Min (Cap: Prepaid, Topups: ".$uptimeTopup."Min)");
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Uptime => Usage total: ".$accountingUsage->{'TotalTimeUsage'}.
"Min (Cap: ".$uptimeLimit->{':='}->{'Value'}."Min, Topups: ".$uptimeTopup."Min)");
}
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Uptime => Usage total: ".$accountingUsage->{'TotalTimeUsage'}.
"Min (Cap: Uncapped, Topups: ".$uptimeTopup."Min)");
}
# If uptime limit exceeded, cap user
if (!(defined($uptimeLimit->{':='}->{'Value'}) && $uptimeLimit->{':='}->{'Value'} == 0)) {
if ($accountingUsage->{'TotalTimeUsage'} >= $alteredUptimeLimit) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage of ".$accountingUsage->{'TotalTimeUsage'}.
"Min exceeds allowed limit of ".$alteredUptimeLimit."Min. Capped.");
return MOD_RES_NACK;
}
}
# Get topups
my $trafficTopup = 0;
if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' is defined");
# Operator: +=
if (defined($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0])) {
# Is it a number?
if ($user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0] =~ /^[0-9]+$/) {
$trafficTopup = $user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0];
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$user->{'ConfigAttributes'}->{$TRAFFIC_TOPUPS_KEY}->[0].
"' is NOT a numeric value");
}
} else {
$server->log(LOG_NOTICE,"[MOD_FEATURE_CAPPING] '".$TRAFFIC_TOPUPS_KEY."' has no value");
}
}
# Set allowed traffic usage
my $alteredTrafficLimit = 0;
# Topups available
if ($trafficTopup > 0) {
if (defined($trafficLimit->{':='}->{'Value'})) {
$alteredTrafficLimit = $trafficLimit->{':='}->{'Value'} + $trafficTopup;
} else {
$alteredTrafficLimit = $trafficTopup;
}
# No topups available
} else {
if (defined($trafficLimit->{':='}->{'Value'})) {
$alteredTrafficLimit = $trafficLimit->{':='}->{'Value'};
}
}
# Traffic usage
if (!(defined($trafficLimit->{':='}->{'Value'}) && $trafficLimit->{':='}->{'Value'} == 0)) {
if (!defined($trafficLimit->{':='}->{'Value'})) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}.
"Mb (Cap: Prepaid, Topups: ".$trafficTopup."Mb)");
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}.
"Mb (Cap: ".$trafficLimit->{':='}->{'Value'}."Mb, Topups: ".$trafficTopup."Mb)");
}
} else {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Bandwidth => Usage total: ".$accountingUsage->{'TotalDataUsage'}.
"Mb (Cap: Uncapped, Topups: ".$trafficTopup."Mb)");
}
# If traffic limit exceeded, cap user
if (!(defined($trafficLimit->{':='}->{'Value'}) && $trafficLimit->{':='}->{'Value'} == 0)) {
if ($accountingUsage->{'TotalDataUsage'} >= $alteredTrafficLimit) {
$server->log(LOG_DEBUG,"[MOD_FEATURE_CAPPING] Usage of ".$accountingUsage->{'TotalDataUsage'}.
"Mb exceeds allowed limit of ".$alteredTrafficLimit."Mb. Capped.");
return MOD_RES_NACK;
}
}
return MOD_RES_ACK;
}
1;
# vim: ts=4
......@@ -8,7 +8,11 @@
#group=
# Filename to store pid of parent process
#pid_file=/var/run/smradiusd.pid
#pid_file=/var/run/smradius/smradiusd.pid
# Cache file
#cache_file=/var/run/smradius/cache
# Uncommenting the below option will prevent awradiusd going into the background
#background=no
......@@ -16,8 +20,8 @@
# Preforking configuration
#
# min_server - Minimum servers to keep around
# min_spare_servers - Minimum spare servers to keep around ready to
# handle requests
# min_spare_servers - Minimum spare servers to keep around ready to
# handle requests
# max_spare_servers - Maximum spare servers to have around doing nothing
# max_servers - Maximum servers alltogether
# max_requests - Maximum number of requests each child will serve
......@@ -40,14 +44,14 @@
# 1 - Warnings and errors
# 2 - Notices, warnings, errors
# 3 - Info, notices, warnings, errors
# 4 - Debugging
# 4 - Debugging
#log_level=2
# File to log to instead of stdout
#log_file=/var/log/smradiusd.log
# Things to log in extreme detail
# modules - Log detailed module running information
# modules - Log detailed module running information
#
# There is no default for this configuration option. Options can be
# separated by commas. ie. modules
......@@ -61,13 +65,51 @@
#timeout=120
# cidr_allow/cidr_deny
# Comma, whitespace or semi-colon separated. Contains a CIDR block to
# compare the clients IP to. If cidr_allow or cidr_deny options are
# given, the incoming client must match a cidr_allow and not match a
# Comma, whitespace or semi-colon separated. Contains a CIDR block to
# compare the clients IP to. If cidr_allow or cidr_deny options are
# given, the incoming client must match a cidr_allow and not match a
# cidr_deny or the client connection will be closed.
#cidr_allow=0.0.0.0/0
#cidr_deny=
# Event timestamp timezone, in "Continent/City" format
# Defaults to "GMT"
event_timezone=GMT
# SMTP server to use when sending email
#smtp_server=127.0.0.1
[radius]
# Use packet timestamp, if unset, the default is to use the server
# timestamp at the moment the packet is received.
#
# WARNING!!!!
# Not all routers keep time, it may occur that some routers depend on
# getting date & time apon reboot from an ntp server. The problem
# will arise when the router cannot get the date and time before the
# first user logs in .. BAM, you'll have sessions with a period key
# in current month but an event timestamp in 1970.
#
# Defaults to "no"
#use_packet_timestamp=yes
# Radius server abuse prevention
#
# Abuse prevention will drop packets which flood the radius server,
# or are duplicated in a short timeframe. You probably want this if
# you are not being fed by a radius proxy.
#
# Defaults to "no"
#use_abuse_prevention=yes
# How fast can a NAS spam the same type of request
#
# Access-Request defaults to 10s
#access_request_abuse_threshold=10
#
# Accounting-Request defaults to 5s
#accounting_request_abuse_threshold=5
[database]
......@@ -91,6 +133,7 @@ load=<<EOT
dicts/dictionary
dicts/dictionary.microsoft
dicts/dictionary.mikrotik
dicts/dictionary.wispr
EOT
......@@ -117,6 +160,10 @@ EOT
[features]
modules=<<EOT
mod_feature_capping
mod_feature_validity
mod_feature_fup
mod_feature_user_stats
mod_feature_update_user_stats_sql
EOT
......@@ -206,12 +253,34 @@ get_topups_query=<<EOT
@TP@users
WHERE
@TP@topups.UserID = @TP@users.ID
AND @TP@topups.ValidFrom >= ?
AND @TP@topups.ValidFrom = ?
AND @TP@topups.ValidTo >= ?
AND @TP@topups.Depleted = 0
AND @TP@users.Username = ?
EOT
topups_add_query=<<EOT
INSERT INTO @TP@topups (
UserID,
Timestamp,
ValidFrom,
ValidTo,
Type,
Value,
Depleted
) VALUES (
%{user.ID},
%{query.Timestamp},
%{query.ValidFrom},
%{query.ValidTo},
%{query.Type},
%{query.Value},
%{query.Depleted}
)
EOT
# MOD_ACCOUNTING_SQL
[mod_accounting_sql]
......@@ -235,11 +304,19 @@ accounting_start_query=<<EOT
AcctStatusType,
NASIdentifier,
NASIPAddress,
AcctDelayTime
AcctDelayTime,
AcctSessionTime,
AcctInputOctets,
AcctInputGigawords,
AcctInputPackets,
AcctOutputOctets,
AcctOutputGigawords,
AcctOutputPackets,
PeriodKey
)
VALUES
(
%{request.User-Name},
%{user.Username},
%{request.Service-Type},
%{request.Framed-Protocol},
%{request.NAS-Port},
......@@ -254,61 +331,117 @@ accounting_start_query=<<EOT
%{request.Acct-Status-Type},
%{request.NAS-Identifier},
%{request.NAS-IP-Address},
%{request.Acct-Delay-Time}
%{request.Acct-Delay-Time},
%{request.Acct-Session-Time},
%{request.Acct-Input-Octets},
%{request.Acct-Input-Gigawords},
%{request.Acct-Input-Packets},
%{request.Acct-Output-Octets},
%{request.Acct-Output-Gigawords},
%{request.Acct-Output-Packets},
%{query.PeriodKey}
)
EOT
accounting_update_get_records_query=<<EOT
SELECT
SUM(AcctInputOctets) AS AcctInputOctets,
SUM(AcctInputPackets) AS AcctInputPackets,
SUM(AcctOutputOctets) AS AcctOutputOctets,
SUM(AcctOutputPackets) AS AcctOutputPackets,
SUM(AcctInputGigawords) AS AcctInputGigawords,
SUM(AcctOutputGigawords) AS AcctOutputGigawords,
SUM(AcctSessionTime) AS AcctSessionTime,
PeriodKey
FROM
@TP@accounting
WHERE
Username = %{user.Username}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
AND NASPort = %{request.NAS-Port}
GROUP BY
PeriodKey
ORDER BY
ID ASC
EOT
accounting_update_query=<<EOT
UPDATE
@TP@accounting
SET
AcctSessionTime = %{request.Acct-Session-Time},
AcctInputOctets = %{request.Acct-Input-Octets},
AcctInputGigawords = %{request.Acct-Input-Gigawords},
AcctInputPackets = %{request.Acct-Input-Packets},
AcctOutputOctets = %{request.Acct-Output-Octets},
AcctOutputGigawords = %{request.Acct-Output-Gigawords},
AcctOutputPackets = %{request.Acct-Output-Packets},
AcctSessionTime = %{query.Acct-Session-Time},
AcctInputOctets = %{query.Acct-Input-Octets},
AcctInputGigawords = %{query.Acct-Input-Gigawords},
AcctInputPackets = %{query.Acct-Input-Packets},
AcctOutputOctets = %{query.Acct-Output-Octets},
AcctOutputGigawords = %{query.Acct-Output-Gigawords},
AcctOutputPackets = %{query.Acct-Output-Packets},
AcctStatusType = %{request.Acct-Status-Type}
WHERE
Username = %{request.User-Name}
Username = %{user.Username}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
AND NASPort = %{request.NAS-Port}
AND PeriodKey = %{query.PeriodKey}
EOT
accounting_stop_query=<<EOT
accounting_stop_status_query=<<EOT
UPDATE
@TP@accounting
SET
AcctSessionTime = %{request.Acct-Session-Time},
AcctInputOctets = %{request.Acct-Input-Octets},
AcctInputGigawords = %{request.Acct-Input-Gigawords},
AcctInputPackets = %{request.Acct-Input-Packets},
AcctOutputOctets = %{request.Acct-Output-Octets},
AcctOutputGigawords = %{request.Acct-Output-Gigawords},
AcctOutputPackets = %{request.Acct-Output-Packets},
AcctStatusType = %{request.Acct-Status-Type},
AcctTerminateCause = %{request.Acct-Terminate-Cause}
WHERE
Username = %{request.User-Name}
Username = %{user.Username}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
AND NASPort = %{request.NAS-Port}
EOT
accounting_usage_query=<<EOT
SELECT
SUM(AcctInputOctets) AS InputOctets,
SUM(AcctOutputOctets) AS OutputOctets,
SUM(AcctInputGigawords) AS InputGigawords,
SUM(AcctOutputGigawords) AS OutputGigawords,
SUM(AcctSessionTime) AS SessionTime
SUM(AcctInputOctets) AS AcctInputOctets,
SUM(AcctOutputOctets) AS AcctOutputOctets,
SUM(AcctInputGigawords) AS AcctInputGigawords,
SUM(AcctOutputGigawords) AS AcctOutputGigawords,
SUM(AcctSessionTime) AS AcctSessionTime
FROM
@TP@accounting
WHERE
Username = %{request.User-Name}
Username = %{user.Username}
AND PeriodKey = %{query.PeriodKey}
EOT
accounting_select_duplicates_query=<<EOT
SELECT
ID
FROM
@TP@accounting
WHERE
Username = %{user.Username}
AND AcctSessionID = %{request.Acct-Session-Id}
AND NASIPAddress = %{request.NAS-IP-Address}
AND NASPort = %{request.NAS-Port}
AND PeriodKey = %{query.PeriodKey}
ORDER BY
ID DESC
LIMIT 99 OFFSET 1
EOT
accounting_delete_duplicates_query=<<EOT
DELETE FROM
@TP@accounting
WHERE
ID = %{query.DuplicateID}
EOT
# This is how long we going to cache the usage query for
# Default: 300 (seconds)
#
# You can use "no", "0", "false" to disable, specify a number > 1, or use
# "yes", "1", "true" to enable with the default value
accounting_usage_cache_time=300
# MOD_USERDB_SQL
......@@ -316,11 +449,11 @@ EOT
userdb_find_query=<<EOT
SELECT
ID
ID, Disabled
FROM
@TP@users
WHERE
Username = %{request.User-Name}
Username = %{user.Username}
EOT
userdb_get_group_attributes_query=<<EOT
......@@ -329,8 +462,9 @@ userdb_get_group_attributes_query=<<EOT
FROM
@TP@group_attributes, @TP@users_to_groups
WHERE
users_to_groups.UserID = %{userdb.id}
users_to_groups.UserID = %{user.ID}
AND group_attributes.GroupID = users_to_groups.GroupID
AND group_attributes.Disabled = 0
EOT
userdb_get_user_attributes_query=<<EOT
......@@ -339,9 +473,82 @@ userdb_get_user_attributes_query=<<EOT
FROM
@TP@user_attributes
WHERE
UserID = %{userdb.id}
UserID = %{user.ID}
AND Disabled = 0
EOT
users_data_set_query=<<EOT
INSERT INTO
@TP@users_data (UserID, LastUpdated, Name, Value)
VALUES
(
%{user.ID},
%{query.LastUpdated},
%{query.Name},
%{query.Value}
)
EOT
users_data_update_query=<<EOT
UPDATE
@TP@users_data
SET
LastUpdated = %{query.LastUpdated},
Value = %{query.Value}
WHERE
UserID = %{user.ID}
AND Name = %{query.Name}
EOT
users_data_get_query=<<EOT
SELECT
LastUpdated, Name, Value
FROM
@TP@users_data
WHERE
UserID = %{user.ID}
AND Name = %{query.Name}
EOT
users_data_delete_query=<<EOT
DELETE FROM
@TP@users_data
WHERE
UserID = %{user.ID}
AND Name = %{query.Name}
EOT
# This is how long we going to cache the data query for
# Default: 300 (seconds)
#
# You can use "no", "0", "false" to disable, specify a number > 1, or use
# "yes", "1", "true" to enable with the default value
userdb_data_cache_time=300
# MOD_FEATURE_UPDATE_USER_STATS_SQL
[mod_feature_update_user_stats_sql]
update_user_stats_query=<<EOT
UPDATE
@TP@users
SET
PeriodKey = %{query.PeriodKey},
TotalTraffic = %{query.TotalTraffic},
TotalUptime = %{query.TotalUptime},
NASIdentifier = %{request.NAS-Identifier},
LastAcctUpdate = now()
WHERE
Username = %{user.Username}
EOT
# MOD_FEATURE_CAPPING
[mod_feature_capping]
# Enable Mikrotik-specific return vattributes
#enable_mikrotik=1
# Enable caveat for SMRadius-Capping-Traffic-Limit having the meaning of 0 and -undef- swapped up to v1.0.x
#caveat_captrafzero=1
# Test harness for module loading
# Copyright (C) 2014-2016, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
use Test::More;
use strict;
use warnings;
require_ok("smradius::daemon");
require_ok("smradius::client");
done_testing();
use strict;
use warnings;
use AWITPT::Util;
use Data::Dumper;
use Date::Parse;
use POSIX qw(:sys_wait_h);
use Test::Most;
use Test::Most::Exception 'throw_failure';
#
# Check that database tests are enabled
#
# We need DBTESTS enabled to run this
if (!$ENV{'DBTESTS'}) {
plan skip_all => 'DBTESTS not set in ENV';
done_testing();
exit 0;
}
#
# Load database handling libraries
#
require_ok('AWITPT::DB::DBILayer');
require_ok('AWITPT::DB::DBLayer');
use AWITPT::DB::DBLayer;
#
# Load our server and client
#
require_ok("smradius::daemon");
require_ok("smradius::client");
#
# Daemon help
#
can_ok("smradius::daemon","displayHelp");
#
# Try connect to database
#
my $dbh = AWITPT::DB::DBILayer->new({
'Username' => 'root',
'DSN' => 'DBI:mysql:database=smradiustest;host=localhost',
});
# If we cannot connect, just bail out
if ($dbh->connect()) {
BAIL_OUT("ERROR: Failed to connect to database for testing purposes: ".$dbh->error());
}
AWITPT::DB::DBLayer::setHandle($dbh);
#
# Make sure DB is clean
#
my $sth;
$sth = DBDo("DELETE FROM topups");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'topups");
$sth = DBDo("DELETE FROM accounting");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'accounting");
$sth = DBDo("DELETE FROM user_attributes");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'user_attributes");
$sth = DBDo("DELETE FROM client_attributes");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'client_attributes");
$sth = DBDo("DELETE FROM users");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'users'");
$sth = DBDo("DELETE FROM clients_to_realms");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'clients_to_realms'");
$sth = DBDo("DELETE FROM clients");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'clients'");
$sth = DBDo("DELETE FROM realms");
is(AWITPT::DB::DBLayer::error(),"","Clean table 'realms'");
#
# Run server and client
#
our $child;
if ($child = fork()) {
# CHLD handler
local $SIG{CHLD} = sub {
warn "SIGCHLD TRIGGER";
waitpid($child,-1);
};
# Install signal handlers to cleanup if we get a TERM or INT
local $SIG{TERM} = local $SIG{INT} = \&cleanup;
# Wait before starting
sleep(2);
# Setup failure handler
set_failure_handler( sub { my @params = @_; cleanup(); throw_failure } );
my $res;
#
# Make sure basic test without any config does not authenticate users
#
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser1',
'User-Password=test123',
);
is(ref($res),"","smradclient ref should return ''");
is($res,1,"smradclient result should be 1");
#
# Create test case data
#
my $client1_ID = testDBInsert("Create client 'localhost'",
"INSERT INTO clients (Name,AccessList,Disabled) VALUES ('localhost','127.0.0.0/8',0)"
);
my $client1attr1_ID = testDBInsert("Create client 'localhost' secret",
"INSERT INTO client_attributes (ClientID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$client1_ID,'SMRadius-Config-Secret',':=','secret123'
);
# Blank realm
my $realm1_ID = testDBInsert("Create realm ''",
"INSERT INTO realms (Name,Disabled) VALUES ('',0)"
);
my $clientTOrealm1_ID = testDBInsert("Link client 'localhost' to realm ''",
"INSERT INTO clients_to_realms (ClientID,RealmID,Disabled) VALUES (?,?,0)",$client1_ID,$realm1_ID
);
#
# Check we get an Access-Accept for a bare user using a blank realm
#
my $user1_ID = testDBInsert("Create user 'testuser1'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser1',0)"
);
my $user1attr1_ID = testDBInsert("Create user 'testuser1' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user1_ID,'User-Password','==','test123'
);
my $user1attr2_ID = testDBInsert("Create user 'testuser1' attribute 'Framed-IP-Address'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user1_ID,'Framed-IP-Address',':=','10.0.0.1'
);
my $user1attr3_ID = testDBInsert("Create user 'testuser1' vendor attribute '[14988:Mikrotik-Rate-Limit]'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user1_ID,'[14988:Mikrotik-Rate-Limit]',':=','1024k/512k'
);
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser1',
'User-Password=test123',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'response'}->{'code'},"Access-Accept","Check our return is 'Access-Accept' for bare user blank '' realm");
# Test the normal attribute and vendor attribute
is($res->{'response'}->{'attributes'}->{'Framed-IP-Address'},"10.0.0.1","Check that attribute 'Framed-IP-Address' is".
" returned");
is($res->{'response'}->{'vattributes'}->{'14988'}->{'Mikrotik-Rate-Limit'}->[0],"1024k/512k","Check that the vendor attribute".
"'14988:Mikrotik-Rate-Limit' is returned");
# Add filter attributes
my $user1attr4_ID = testDBInsert("Create user 'testuser1' filter attribute for 'Framed-IP-Address'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user1_ID,'SMRadius-Config-Filter-Reply-Attribute',':=','Framed-IP-Address'
);
my $user1attr5_ID = testDBInsert("Create user 'testuser1' filter vattribute for 'Mikrotik-Rate-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user1_ID,'SMRadius-Config-Filter-Reply-VAttribute',':=','Mikrotik-Rate-Limit'
);
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser1',
'User-Password=test123',
);
is(ref($res),"HASH","smradclient should return a HASH");
# We shouldn't....
isnt($res->{'response'}->{'attributes'}->{'Framed-IP-Address'},"10.0.0.1","Check that attribute 'Framed-IP-Address' is".
" returned");
#
# Modify data for the default realm
#
testDBDelete("Delete blank realm '' link to client",
"DELETE FROM clients_to_realms WHERE ID = ?",$clientTOrealm1_ID
);
testDBDelete("Delete blank realm '' for realm '<DEFAULT>' test",
"DELETE FROM realms WHERE ID = ?",$realm1_ID
);
my $realm1b_ID = testDBInsert("Create default realm '<DEFAULT>'",
"INSERT INTO realms (Name,Disabled) VALUES ('<DEFAULT>',0)"
);
my $clientTOrealm1b_ID = testDBInsert("Link client 'localhost' to realm '<DEFAULT>'",
"INSERT INTO clients_to_realms (ClientID,RealmID,Disabled) VALUES (?,?,0)",$client1_ID,$realm1b_ID
);
#
# Check we get an Access-Accept for a bare user using the default realm
#
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser1',
'User-Password=test123',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'response'}->{'code'},"Access-Accept","Check our return is 'Access-Accept' for bare user on <DEFAULT> realm");
#
# Check we get a Access-Reject for a traffic topup user
#
my $user2_ID = testDBInsert("Create user 'testuser2'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser2',0)"
);
my $user2attr1_ID = testDBInsert("Create user 'testuser2' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user2_ID,'User-Password','==','test123'
);
my $user2attr2_ID = testDBInsert("Create user 'testuser2' attribute 'SMRadius-Capping-Traffic-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user2_ID,'SMRadius-Capping-Traffic-Limit',':=','0'
);
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser2',
'User-Password=test123',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'response'}->{'code'},"Access-Reject","Check our return is 'Access-Reject' for a traffic topup user");
#
# Check we get a Access-Reject for a uptime topup user
#
my $user2b_ID = testDBInsert("Create user 'testuser2b'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser2b',0)"
);
my $user2battr1_ID = testDBInsert("Create user 'testuser2b' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user2b_ID,'User-Password','==','test123'
);
my $user2battr3_ID = testDBInsert("Create user 'testuser2b' attribute 'SMRadius-Capping-Uptime-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user2b_ID,'SMRadius-Capping-Uptime-Limit',':=','0'
);
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser2b',
'User-Password=test123',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'response'}->{'code'},"Access-Reject","Check our return is 'Access-Reject' for a uptime topup user");
#
# Test accounting START packet
#
my $session1_ID = "09d15244";
my $session1_Timestamp = time();
my $session1_Timestamp_str = DateTime->from_epoch(epoch => $session1_Timestamp,time_zone => 'UTC')
->strftime('%Y-%m-%d %H:%M:%S');
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"acct",
"secret123",
'NAS-IP-Address=10.0.0.3',
'Acct-Delay-Time=11',
'NAS-Identifier=Test-NAS1',
'Acct-Status-Type=Start',
'Event-Timestamp='.$session1_Timestamp,
'Framed-IP-Address=10.0.1.3',
'Acct-Session-Id='.$session1_ID,
'NAS-Port-Id=test iface name1',
'Called-Station-Id=testservice1',
'Calling-Station-Id=00:00:0C:EE:47:AA',
'User-Name=testuser1',
'NAS-Port-Type=Ethernet',
'NAS-Port=45355555',
'Framed-Protocol=PPP',
'Service-Type=Framed-User',
);
is(ref($res),"HASH","smradclient should return a HASH");
testDBResults("Check accounting record is created correctly",'accounting',{'AcctSessionID' => $session1_ID},
{
'NASIPAddress' => '10.0.0.3',
'AcctDelayTime' => '11',
'NASIdentifier' => 'Test-NAS1',
'AcctStatusType' => 1,
'EventTimestamp' => sub{ _timestampCheck(shift,$session1_Timestamp_str) },
'FramedIPAddress' => '10.0.1.3',
'AcctSessionId' => $session1_ID,
'NASPortId' => 'test iface name1',
'CalledStationId' => 'testservice1',
'CallingStationId' => '00:00:0C:EE:47:AA',
'Username' => 'testuser1',
'NASPortType' => 15,
'NASPort' => '45355555',
'FramedProtocol' => 1,
'ServiceType' => 2,
'AcctOutputPackets' => undef,
'AcctOutputGigawords' => undef,
'AcctOutputOctets' => undef,
'AcctInputPackets' => undef,
'AcctInputGigawords' => undef,
'AcctInputOctets' => undef,
'AcctSessionTime' => undef,
}
);
#
# Test accounting ALIVE packet
#
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"acct",
"secret123",
'Acct-Status-Type=Interim-Update',
'Acct-Output-Packets=800000',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=810000000',
'Acct-Input-Packets=777777',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=123456789',
'Acct-Session-Time=999',
'User-Name=testuser1',
'Acct-Session-Id='.$session1_ID,
'NAS-IP-Address=10.0.0.3',
'NAS-Port=45355555',
);
is(ref($res),"HASH","smradclient should return a HASH");
testDBResults("Check accounting record is updated correctly",'accounting',{'AcctSessionID' => $session1_ID},
{
'NASIPAddress' => '10.0.0.3',
'AcctDelayTime' => '11',
'NASIdentifier' => 'Test-NAS1',
'AcctStatusType' => 3,
'EventTimestamp' => sub{ _timestampCheck(shift,$session1_Timestamp_str) },
'FramedIPAddress' => '10.0.1.3',
'AcctSessionId' => $session1_ID,
'NASPortId' => 'test iface name1',
'CalledStationId' => 'testservice1',
'CallingStationId' => '00:00:0C:EE:47:AA',
'Username' => 'testuser1',
'NASPortType' => 15,
'NASPort' => '45355555',
'FramedProtocol' => 1,
'ServiceType' => 2,
'AcctOutputPackets' => '800000',
'AcctOutputGigawords' => '0',
'AcctOutputOctets' => '810000000',
'AcctInputPackets' => '777777',
'AcctInputGigawords' => '0',
'AcctInputOctets' => '123456789',
'AcctSessionTime' => '999',
}
);
#
# Test accounting STOP packet
#
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"acct",
"secret123",
'Acct-Status-Type=Stop',
'Acct-Output-Packets=999999',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=888888888',
'Acct-Input-Packets=1111111',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=222222222',
'Acct-Session-Time=3998',
'Acct-Terminate-Cause=Session-Timeout',
'User-Name=testuser1',
'Acct-Session-Id='.$session1_ID,
'NAS-IP-Address=10.0.0.3',
'NAS-Port=45355555',
);
is(ref($res),"HASH","smradclient should return a HASH");
testDBResults("Check accounting record is stopped correctly",'accounting',{'AcctSessionID' => $session1_ID},
{
'NASIPAddress' => '10.0.0.3',
'AcctDelayTime' => '11',
'NASIdentifier' => 'Test-NAS1',
'AcctStatusType' => 2,
'EventTimestamp' => sub{ _timestampCheck(shift,$session1_Timestamp_str) },
'FramedIPAddress' => '10.0.1.3',
'AcctSessionId' => $session1_ID,
'NASPortId' => 'test iface name1',
'CalledStationId' => 'testservice1',
'CallingStationId' => '00:00:0C:EE:47:AA',
'Username' => 'testuser1',
'NASPortType' => 15,
'NASPort' => '45355555',
'FramedProtocol' => 1,
'ServiceType' => 2,
'AcctOutputPackets' => '999999',
'AcctOutputGigawords' => '0',
'AcctOutputOctets' => '888888888',
'AcctInputPackets' => '1111111',
'AcctInputGigawords' => '0',
'AcctInputOctets' => '222222222',
'AcctSessionTime' => '3998',
'AcctTerminateCause' => '5',
}
);
#
# Test missing accounting START packet
#
my $session2_ID = "817a0f1b";
my $session2_Timestamp = time();
my $session2_Timestamp_str = DateTime->from_epoch(epoch => $session2_Timestamp,time_zone => 'UTC')
->strftime('%Y-%m-%d %H:%M:%S');
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"acct",
"secret123",
'User-Name=testuser2',
'NAS-IP-Address=10.0.0.1',
'Acct-Delay-Time=12',
'NAS-Identifier=Test-NAS2',
'Acct-Status-Type=Interim-Update',
'Acct-Output-Packets=786933',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=708163705',
'Acct-Input-Packets=670235',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=102600046',
'Acct-Session-Time=800',
'Event-Timestamp='.$session2_Timestamp,
'Framed-IP-Address=10.0.1.1',
'Acct-Session-Id='.$session2_ID,
'NAS-Port-Id=wlan1',
'Called-Station-Id=testservice2',
'Calling-Station-Id=00:00:0C:EE:47:BF',
'NAS-Port-Type=Ethernet',
'NAS-Port=15729175',
'Framed-Protocol=PPP',
'Service-Type=Framed-User',
);
is(ref($res),"HASH","smradclient should return a HASH");
testDBResults("Check missing accounting record is created correctly",'accounting',{'AcctSessionID' => $session2_ID},
{
'Username' => 'testuser2',
'NASIPAddress' => '10.0.0.1',
'AcctDelayTime' => '12',
'NASIdentifier' => 'Test-NAS2',
'AcctStatusType' => 3,
'AcctOutputPackets' => '786933',
'AcctOutputGigawords' => '0',
'AcctOutputOctets' => '708163705',
'AcctInputPackets' => '670235',
'AcctInputGigawords' => '0',
'AcctInputOctets' => '102600046',
'AcctSessionTime' => '800',
'EventTimestamp' => sub{ _timestampCheck(shift,$session2_Timestamp_str) },
'FramedIPAddress' => '10.0.1.1',
'AcctSessionId' => $session2_ID,
'NASPortId' => 'wlan1',
'CalledStationId' => 'testservice2',
'CallingStationId' => '00:00:0C:EE:47:BF',
'NASPortType' => 15,
'NASPort' => '15729175',
'FramedProtocol' => 1,
'ServiceType' => 2,
}
);
#
# Check we get a Access-Accept for a traffic autotopup user
#
my $topuptest1_amount = 100;
my $user3_ID = testDBInsert("Create user 'testuser3'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser3',0)"
);
my $user3attr1_ID = testDBInsert("Create user 'testuser3' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3_ID,'User-Password','==','test456'
);
my $user3attr2_ID = testDBInsert("Create user 'testuser3' attribute 'SMRadius-AutoTopup-Traffic-Enabled'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3_ID,'SMRadius-AutoTopup-Traffic-Enabled',':=','yes'
);
my $user3attr3_ID = testDBInsert("Create user 'testuser3' attribute 'SMRadius-AutoTopup-Traffic-Amount'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3_ID,'SMRadius-AutoTopup-Traffic-Amount',':=',$topuptest1_amount
);
my $user3attr4_ID = testDBInsert("Create user 'testuser3' attribute 'SMRadius-AutoTopup-Traffic-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3_ID,'SMRadius-AutoTopup-Traffic-Limit',':=','500'
);
my $user3attr5_ID = testDBInsert("Create user 'testuser3' attribute 'SMRadius-Capping-Traffic-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3_ID,'SMRadius-Capping-Traffic-Limit',':=','0'
);
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser3',
'User-Password=test456',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'response'}->{'code'},"Access-Accept","Check our return is 'Access-Accept' for a traffic autotopup user");
# Get the time now
my $topuptest1_now = time();
my $topuptest1 = DateTime->from_epoch( 'epoch' => $topuptest1_now, 'time_zone' => 'UTC');
# Use truncate to set all values after 'month' to their default values
my $topuptest1_thisMonth = $topuptest1->clone()->truncate( to => "month" );
# This month, in string form
my $topuptest1_thisMonth_str = $topuptest1_thisMonth->strftime("%Y-%m-%d %H:%M:%S");
# Next month..
my $topuptest1_nextMonth = $topuptest1_thisMonth->clone()->add( months => 1 );
my $topuptest1_nextMonth_str = $topuptest1_nextMonth->strftime("%Y-%m-%d %H:%M:%S");
testDBResults("Check autotopup is added correctly",'topups',{'UserID' => $user3_ID},
{
'UserID' => $user3_ID,
'Timestamp' => sub { return _timestampCheck(shift,$topuptest1_now) },
'Type' => 5,
'ValidFrom' => $topuptest1_thisMonth_str,
'ValidTo' => $topuptest1_nextMonth_str,
'Value' => $topuptest1_amount,
'Depleted' => 0,
'SMAdminDepletedOn' => undef,
}
);
#
# Check we get a Access-Accept for a uptime autotopup user
#
my $topuptest1b_amount = 100;
my $user3b_ID = testDBInsert("Create user 'testuser3b'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser3b',0)"
);
my $user3battr1_ID = testDBInsert("Create user 'testuser3b' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3b_ID,'User-Password','==','test456'
);
my $user3battr2_ID = testDBInsert("Create user 'testuser3b' attribute 'SMRadius-AutoTopup-Uptime-Enabled'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3b_ID,'SMRadius-AutoTopup-Uptime-Enabled',':=','yes'
);
my $user3battr3_ID = testDBInsert("Create user 'testuser3b' attribute 'SMRadius-AutoTopup-Uptime-Amount'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3b_ID,'SMRadius-AutoTopup-Uptime-Amount',':=',$topuptest1b_amount
);
my $user3battr4_ID = testDBInsert("Create user 'testuser3b' attribute 'SMRadius-AutoTopup-Uptime-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3b_ID,'SMRadius-AutoTopup-Uptime-Limit',':=','500'
);
my $user3battr5_ID = testDBInsert("Create user 'testuser3b' attribute 'SMRadius-Capping-Uptime-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user3b_ID,'SMRadius-Capping-Uptime-Limit',':=','0'
);
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"auth",
"secret123",
'User-Name=testuser3b',
'User-Password=test456',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'response'}->{'code'},"Access-Accept","Check our return is 'Access-Accept' for a uptime autotopup user");
# Get the time now
my $topuptest1b_now = time();
my $topuptest1b = DateTime->from_epoch( 'epoch' => $topuptest1b_now, 'time_zone' => 'UTC');
# Use truncate to set all values after 'month' to their default values
my $topuptest1b_thisMonth = $topuptest1b->clone()->truncate( to => "month" );
# This month, in string form
my $topuptest1b_thisMonth_str = $topuptest1b_thisMonth->strftime("%Y-%m-%d %H:%M:%S");
# Next month..
my $topuptest1b_nextMonth = $topuptest1b_thisMonth->clone()->add( months => 1 );
my $topuptest1b_nextMonth_str = $topuptest1b_nextMonth->strftime("%Y-%m-%d %H:%M:%S");
testDBResults("Check autotopup is added correctly",'topups',{'UserID' => $user3b_ID},
{
'UserID' => $user3b_ID,
'Timestamp' => sub { return _timestampCheck(shift,$topuptest1b_now) },
'Type' => 6,
'ValidFrom' => $topuptest1b_thisMonth_str,
'ValidTo' => $topuptest1b_nextMonth_str,
'Value' => $topuptest1b_amount,
'Depleted' => 0,
'SMAdminDepletedOn' => undef,
}
);
#
# Check that if we send an accounting ALIVE we update the auto-topups
#
my $topuptest2_amount = 100;
my $user4_ID = testDBInsert("Create user 'testuser4'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser4',0)"
);
my $user4attr1_ID = testDBInsert("Create user 'testuser4' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user4_ID,'User-Password','==','test456'
);
my $user4attr2_ID = testDBInsert("Create user 'testuser4' attribute 'SMRadius-AutoTopup-Traffic-Enabled'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user4_ID,'SMRadius-AutoTopup-Traffic-Enabled',':=','yes'
);
my $user4attr3_ID = testDBInsert("Create user 'testuser4' attribute 'SMRadius-AutoTopup-Traffic-Amount'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user4_ID,'SMRadius-AutoTopup-Traffic-Amount',':=',$topuptest2_amount
);
my $user4attr4_ID = testDBInsert("Create user 'testuser4' attribute 'SMRadius-AutoTopup-Traffic-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user4_ID,'SMRadius-AutoTopup-Traffic-Limit',':=','500'
);
my $user4attr5_ID = testDBInsert("Create user 'testuser4' attribute 'SMRadius-Capping-Traffic-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user4_ID,'SMRadius-Capping-Traffic-Limit',':=','0'
);
my $user4attr6_ID = testDBInsert("Create user 'testuser4' attribute 'SMRadius-AutoTopup-Traffic-Notify'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user4_ID,'SMRadius-AutoTopup-Traffic-Notify',':=','root@localhost'
);
my $session3_ID = "9c5f24a";
my $session3_Timestamp = time();
$res = smradius::client->run(
"--raddb","dicts",
"127.0.0.1",
"acct",
"secret123",
'User-Name=testuser4',
'NAS-IP-Address=10.0.0.1',
'Acct-Delay-Time=12',
'NAS-Identifier=Test-NAS2',
'Acct-Status-Type=Interim-Update',
'Acct-Output-Packets=786933',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=708163705',
'Acct-Input-Packets=670235',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=102600046',
'Acct-Session-Time=800',
'Event-Timestamp='.$session3_Timestamp,
'Framed-IP-Address=10.0.1.1',
'Acct-Session-Id='.$session3_ID,
'NAS-Port-Id=wlan1',
'Called-Station-Id=testservice2',
'Calling-Station-Id=00:00:0C:EE:47:BF',
'NAS-Port-Type=Ethernet',
'NAS-Port=15729175',
'Framed-Protocol=PPP',
'Service-Type=Framed-User',
);
is(ref($res),"HASH","smradclient should return a HASH");
# Get the time now
my $topuptest2_now = time();
my $topuptest2 = DateTime->from_epoch( 'epoch' => $topuptest2_now, 'time_zone' => 'UTC');
# Use truncate to set all values after 'month' to their default values
my $topuptest2_thisMonth = $topuptest2->clone()->truncate( to => "month" );
# This month, in string form
my $topuptest2_thisMonth_str = $topuptest2_thisMonth->strftime("%Y-%m-%d %H:%M:%S");
# Next month..
my $topuptest2_nextMonth = $topuptest2_thisMonth->clone()->add( months => 1 );
my $topuptest2_nextMonth_str = $topuptest2_nextMonth->strftime("%Y-%m-%d %H:%M:%S");
testDBResults("Check autotopup is added correctly after acct_log",'topups',{'UserID' => $user4_ID},
{
'UserID' => $user4_ID,
'Timestamp' => sub { return _timestampCheck(shift,$topuptest2_now) },
'Type' => 5,
'ValidFrom' => $topuptest2_thisMonth_str,
'ValidTo' => $topuptest2_nextMonth_str,
'Value' => $topuptest2_amount,
'Depleted' => 0,
'SMAdminDepletedOn' => undef,
}
);
#
# Check that if we send an accounting ALIVE we do not trigger FUP
#
my $user5_ID = testDBInsert("Create user 'testuser5'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser5',0)"
);
my $user5attr1_ID = testDBInsert("Create user 'testuser5' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user5_ID,'User-Password','==','test456'
);
my $user5attr2_ID = testDBInsert("Create user 'testuser5' attribute 'SMRadius-FUP-Period'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user5_ID,'SMRadius-FUP-Period',':=','1'
);
my $user5attr3_ID = testDBInsert("Create user 'testuser5' attribute 'SMRadius-FUP-Traffic-Threshold'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user5_ID,'SMRadius-FUP-Traffic-Threshold',':=',800
);
# Add an attribute so we can check the FUP match results
my $user5attr4_ID = testDBInsert("Create user 'testuser5' attribute 'SMRadius-Evaluate'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user5_ID,'SMRadius-Evaluate','||+=',"SMRadius_FUP > 0 ? [14988:Mikrotik-Rate-Limit] = 1638k/8m : [14988:Mikrotik-Rate-Limit] = 1k/1m"
);
my $session4_ID = "a8abc40";
my $session4_Timestamp = time();
$res = smradius::client->run(
"--raddb","dicts",
"--listen","127.0.0.1:1700",
"127.0.0.1",
"acct",
"secret123",
'User-Name=testuser5',
'NAS-IP-Address=10.0.0.1',
'Acct-Delay-Time=12',
'NAS-Identifier=Test-NAS2',
'Acct-Status-Type=Interim-Update',
'Acct-Output-Packets=786933',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=708163705',
'Acct-Input-Packets=670235',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=102600046',
'Acct-Session-Time=800',
'Event-Timestamp='.$session4_Timestamp,
'Framed-IP-Address=10.0.1.1',
'Acct-Session-Id='.$session4_ID,
'NAS-Port-Id=wlan1',
'Called-Station-Id=testservice2',
'Calling-Station-Id=00:00:0C:EE:47:BF',
'NAS-Port-Type=Ethernet',
'NAS-Port=15729175',
'Framed-Protocol=PPP',
'Service-Type=Framed-User',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'listen'}->{'response'}->{'code'},"CoA-Request","Check that the packet we got back is infact a ".
"CoA-Request");
is($res->{'listen'}->{'response'}->{'vattributes'}->{'14988'}->{'Mikrotik-Rate-Limit'}->[0],"1k/1m","Check that the vendor attribute".
"'14988:Mikrotik-Rate-Limit' is returned on the negative side of the IF");
testDBResults("Check FUP state was added to the user stats table as 0",'users_data',
{'UserID' => $user5_ID, 'Name' => "mod_feature_fup/State"},
{'Value' => "0"},
1, # Disable order
);
#
# Check that if we send an accounting ALIVE with a usage amount that exceeds FUP, that we trigger it
#
my $user6_ID = testDBInsert("Create user 'testuser6'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser6',0)"
);
my $user6attr1_ID = testDBInsert("Create user 'testuser6' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user6_ID,'User-Password','==','test456'
);
my $user6attr2_ID = testDBInsert("Create user 'testuser6' attribute 'SMRadius-FUP-Period'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user6_ID,'SMRadius-FUP-Period',':=','1'
);
my $user6attr3_ID = testDBInsert("Create user 'testuser6' attribute 'SMRadius-FUP-Traffic-Threshold'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user6_ID,'SMRadius-FUP-Traffic-Threshold',':=',800
);
# Add an attribute so we can check the FUP match results
my $user6attr4_ID = testDBInsert("Create user 'testuser6' attribute 'SMRadius-Evaluate'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user6_ID,'SMRadius-Evaluate','||+=',"SMRadius_FUP > 0 ? [14988:Mikrotik-Rate-Limit] = 1638k/8m : [14988:Mikrotik-Rate-Limit] = 1k/1m"
);
my $session5_ID = "582dc00";
my $session5_Timestamp = time();
$res = smradius::client->run(
"--raddb","dicts",
"--listen","127.0.0.1:1700",
"127.0.0.1",
"acct",
"secret123",
'User-Name=testuser6',
'NAS-IP-Address=10.0.0.1',
'Acct-Delay-Time=12',
'NAS-Identifier=Test-NAS2',
'Acct-Status-Type=Interim-Update',
'Acct-Output-Packets=786933',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=808163705',
'Acct-Input-Packets=670235',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=202600046',
'Acct-Session-Time=800',
'Event-Timestamp='.$session5_Timestamp,
'Framed-IP-Address=10.0.1.1',
'Acct-Session-Id='.$session5_ID,
'NAS-Port-Id=wlan1',
'Called-Station-Id=testservice2',
'Calling-Station-Id=00:00:0C:EE:47:BF',
'NAS-Port-Type=Ethernet',
'NAS-Port=15729175',
'Framed-Protocol=PPP',
'Service-Type=Framed-User',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'listen'}->{'response'}->{'code'},"CoA-Request","Check that the packet we got back is infact a ".
"CoA-Request");
is($res->{'listen'}->{'response'}->{'vattributes'}->{'14988'}->{'Mikrotik-Rate-Limit'}->[0],"1638k/8m","Check that the ".
"vendor attribute '14988:Mikrotik-Rate-Limit' is returned on the success side of the FUP check");
testDBResults("Check FUP state was added to the user stats table as 1",'users_data',
{'UserID' => $user6_ID, 'Name' => "mod_feature_fup/State"},
{'Value' => "1"},
1, # Disable order
);
#
# Check that if we send an accounting ALIVE with a usage amount that exceeds capping, that we trigger a POD
#
my $user7_ID = testDBInsert("Create user 'testuser7'",
"INSERT INTO users (UserName,Disabled) VALUES ('testuser7',0)"
);
my $user7attr1_ID = testDBInsert("Create user 'testuser7' attribute 'User-Password'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user7_ID,'User-Password','==','test456'
);
# Add an attribute so we can check the capping does a POD
my $user7attr4_ID = testDBInsert("Create user 'testuser7' attribute 'SMRadius-Capping-Traffic-Limit'",
"INSERT INTO user_attributes (UserID,Name,Operator,Value,Disabled) VALUES (?,?,?,?,0)",
$user7_ID,'SMRadius-Capping-Traffic-Limit',':=','1'
);
my $session6_ID = "5209dac0";
my $session6_Timestamp = time();
$res = smradius::client->run(
"--raddb","dicts",
"--listen","127.0.0.1:1700",
"127.0.0.1",
"acct",
"secret123",
'User-Name=testuser7',
'NAS-IP-Address=10.0.0.1',
'Acct-Delay-Time=12',
'NAS-Identifier=Test-NAS2',
'Acct-Status-Type=Interim-Update',
'Acct-Output-Packets=786933',
'Acct-Output-Gigawords=0',
'Acct-Output-Octets=808163705',
'Acct-Input-Packets=670235',
'Acct-Input-Gigawords=0',
'Acct-Input-Octets=202600046',
'Acct-Session-Time=800',
'Event-Timestamp='.$session6_Timestamp,
'Framed-IP-Address=10.0.1.1',
'Acct-Session-Id='.$session6_ID,
'NAS-Port-Id=wlan1',
'Called-Station-Id=testservice2',
'Calling-Station-Id=00:00:0C:EE:47:BF',
'NAS-Port-Type=Ethernet',
'NAS-Port=15729175',
'Framed-Protocol=PPP',
'Service-Type=Framed-User',
);
is(ref($res),"HASH","smradclient should return a HASH");
is($res->{'listen'}->{'response'}->{'code'},"Disconnect-Request","Check that the packet we got back is infact a ".
"Disconnect-Request");
sleep(5);
} else {
smradius::daemon->run(
"--fg",
"--debug",
"--config", "smradiusd.conf.test",
);
sleep 4;
exit 0;
}
cleanup();
done_testing();
# Cleanup function
sub cleanup
{
if ($child) {
# Kill the child if it exists
if (kill(0,$child)) {
kill('TERM',$child);
}
# Wait for it to be reaped
waitpid($child,-1);
}
}
# Function to quickly and easily insert data into the DB and generate 2 tests out of it
sub testDBInsert
{
my ($name,@params) = @_;
# Do the work...
DBDo(@params);
# Make sure we got no error
is(AWITPT::DB::DBLayer::error(),"",$name);
# Grab the last insert ID
my $id = DBLastInsertID();
# Make sure its > 0
is($id > 0,1,"$name, insert ID > 0");
return $id;
}
# Function to quickly and easily delete data from the DB
sub testDBDelete
{
my ($name,@params) = @_;
# Do the work...
DBDo(@params);
# Make sure we got no error
is(AWITPT::DB::DBLayer::error(),"",$name);
return 1;
}
# Test DB select results
sub testDBResults
{
my ($name,$table,$where,$resultCheck,$disableOrder) = @_;
# Build column list
my $columnList_str = join(',',keys %{$resultCheck});
# Create where criteria
my @whereLines = ();
my @whereData = ();
foreach my $columnName (keys %{$where}) {
# Add template placeholders
push(@whereLines,"$columnName = ?");
# Add data for template placeholders
push(@whereData,$where->{$columnName});
}
my $whereLines_str = join(' AND ',@whereLines);
# Check if we're not disabling ordering
my $extraSQL = "";
if (!defined($disableOrder)) {
$extraSQL = "ORDER BY ID DESC";
}
my $sqlQuery = "
SELECT
$columnList_str
FROM
$table
WHERE
$whereLines_str
$extraSQL
";
# Do select
my $sth = DBSelect($sqlQuery,@whereData);
# Make sure we got no error
is(AWITPT::DB::DBLayer::error(),"","Errors on DBSelect ($sqlQuery interpolated with ".join(', ',$extraSQL).": $name");
# We should get one result...
my $row = hashifyLCtoMC($sth->fetchrow_hashref(),keys %{$resultCheck});
is(defined($row),1,"DBSelect row defined: $name");
# Loop through results and check if they match
foreach my $resultName (keys %{$resultCheck}) {
# Check if the result is a code-based subroutine
if (ref(my $result = $resultCheck->{$resultName}) eq "CODE") {
is($resultCheck->{$resultName}($row->{$resultName}),1,"$name: $resultName sub{} check");
} else {
is($row->{$resultName},$resultCheck->{$resultName},"$name: $resultName check");
}
}
}
sub _timestampCheck
{
my ($testVal,$rightVal) = @_;
# Make sure testVal is defined
return "_timestampCheck: NO \$testVal" if (!defined($testVal));
# Make sure $testVal_time is returned form str2time
my $testVal_time = str2time($testVal,'UTC');
# Check if $rightVal is defined
my $rightVal_time;
if (!defined($rightVal)) {
$rightVal_time = time();
} elsif ($rightVal =~ /^\d+$/) {
$rightVal_time = $rightVal;
} else {
$rightVal_time = str2time($rightVal,'UTC');
}
# Make sure rightVal_time is defined
return "_timestampCheck: NO \$rightVal_time" if (!defined($rightVal_time));
# Grab the absolute difference
my $diff = abs($testVal_time - $rightVal_time);
return ($diff < 10) // "TIME DEVIATION: $diff";
}
#!/bin/bash
git submodule update --recursive --init
pushd 3rdparty/awitpt
git checkout v1.0.x
git pull
popd
pushd webgui/awitef
git checkout v0.0.x
git pull
popd
<?php
# Ajax to PHP
# Copyright (C) 2007-2015, AllWorldIT
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# Requires / Includes
include_once("include/ajax/json.php");
......@@ -48,7 +65,8 @@
);
echo json_encode($res);
*/
echo json_encode($msg);
// echo json_encode($msg);
jsonError(-1,$msg);
exit;
}
......@@ -176,6 +194,8 @@
$res = addAdminClientRealm($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -185,6 +205,8 @@
$res = removeAdminClientRealm($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -211,6 +233,8 @@
$res = updateAdminClient($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -220,6 +244,8 @@
$res = createAdminClient($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -229,6 +255,8 @@
$res = removeAdminClient($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -268,6 +296,8 @@
$res = addAdminClientAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -277,6 +307,8 @@
$res = updateAdminClientAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -319,6 +351,8 @@
$res = removeAdminClientAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -332,12 +366,14 @@
$res = new json_response;
$res->addField('uptimeCap','int');
$res->addField('trafficCap','int');
$res->addField('trafficCurrentTopupUsed','int');
$res->addField('trafficCurrentTopupCap','int');
$res->addField('uptimeCurrentTopupUsed','int');
$res->addField('uptimeCurrentTopupCap','int');
$res->addField('trafficTopupRemaining','int');
$res->addField('uptimeTopupRemaining','int');
$res->addField('trafficUsage','int');
$res->addField('uptimeUsage','int');
$res->addField('trafficTopups','int');
$res->addField('uptimeTopups','int');
$res->addField('TotalTrafficTopups','int');
$res->addField('TotalUptimeTopups','int');
$res->addField('AllTrafficTopups','array');
$res->addField('AllUptimeTopups','array');
$res->parseHash($rawData);
$res->setDatasetSize($numResults);
......@@ -353,12 +389,14 @@
$res = new json_response;
$res->addField('uptimeCap','int');
$res->addField('trafficCap','int');
$res->addField('trafficCurrentTopupUsed','int');
$res->addField('trafficCurrentTopupCap','int');
$res->addField('uptimeCurrentTopupUsed','int');
$res->addField('uptimeCurrentTopupCap','int');
$res->addField('trafficTopupRemaining','int');
$res->addField('uptimeTopupRemaining','int');
$res->addField('trafficUsage','int');
$res->addField('uptimeUsage','int');
$res->addField('trafficTopups','int');
$res->addField('uptimeTopups','int');
$res->addField('TotalTrafficTopups','int');
$res->addField('TotalUptimeTopups','int');
$res->addField('AllTrafficTopups','array');
$res->addField('AllUptimeTopups','array');
$res->parseHash($rawData);
$res->setDatasetSize($numResults);
......@@ -392,6 +430,8 @@
$res = createAdminUserTopup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -401,6 +441,8 @@
$res = updateAdminUserTopup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -425,6 +467,8 @@
$res = removeAdminUserTopup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -455,6 +499,8 @@
$res = createWiSPUserTopup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -464,6 +510,8 @@
$res = updateWiSPUserTopup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -488,6 +536,8 @@
$res = removeWiSPUserTopup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -515,6 +565,8 @@
$res = removeAdminGroupMember($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -541,6 +593,8 @@
$res = removeAdminRealmMember($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -564,8 +618,8 @@
$res->addField('CalledStationID','string');
$res->addField('AcctSessionID','string');
$res->addField('FramedIPAddress','string');
$res->addField('AcctInputMbyte','int');
$res->addField('AcctOutputMbyte','int');
$res->addField('AcctInput','float');
$res->addField('AcctOutput','float');
$res->addField('AcctSessionTime','int');
$res->addField('ConnectTermReason','string');
$res->parseArray($rawData);
......@@ -581,6 +635,8 @@
$res = addAdminUserGroup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -590,6 +646,8 @@
$res = removeAdminUserGroup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -616,6 +674,8 @@
$res = addAdminRealmAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -625,6 +685,8 @@
$res = updateAdminRealmAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -667,6 +729,8 @@
$res = removeAdminRealmAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -677,6 +741,8 @@
$res = addAdminGroupAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -686,6 +752,8 @@
$res = updateAdminGroupAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -728,6 +796,8 @@
$res = removeAdminGroupAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -738,6 +808,8 @@
$res = addAdminUserAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -747,6 +819,8 @@
$res = updateAdminUserAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -789,6 +863,8 @@
$res = removeAdminUserAttribute($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -799,6 +875,8 @@
$res = updateWiSPUser($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -808,6 +886,8 @@
$res = createWiSPUser($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -817,6 +897,8 @@
$res = removeWiSPUser($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -849,6 +931,7 @@
$res->setID('ID');
$res->addField('ID','int');
$res->addField('Username','string');
$res->addField('Disabled','boolean');
$res->addField('Password','string');
$res->addField('Firstname','string');
$res->addField('Lastname','string');
......@@ -861,6 +944,36 @@
echo json_encode($res->export());
break;
case "getWiSPUserAttributes":
$res = getWiSPUserAttributes($soapParams);
$rawData = $res[0]; $numResults = $res[1];
$res = new json_response;
$res->setID('ID');
$res->addField('ID','int');
$res->addField('Name','string');
$res->addField('Operator','string');
$res->addField('Value','string');
$res->parseArray($rawData);
$res->setDatasetSize($numResults);
echo json_encode($res->export());
break;
case "getWiSPUserGroups":
$res = getWiSPUserGroups($soapParams);
$rawData = $res[0]; $numResults = $res[1];
$res = new json_response;
$res->setID('ID');
$res->addField('ID','int');
$res->addField('Name','string');
$res->parseArray($rawData);
$res->setDatasetSize($numResults);
echo json_encode($res->export());
break;
# WiSPUserLogs.js functions
case "getWiSPUserLogs":
......@@ -880,8 +993,9 @@
$res->addField('CalledStationID','string');
$res->addField('AcctSessionID','string');
$res->addField('FramedIPAddress','string');
$res->addField('AcctInputMbyte','int');
$res->addField('AcctOutputMbyte','int');
$res->addField('AcctInput','float');
$res->addField('AcctOutput','float');
$res->addField('AcctSessionTime','int');
$res->addField('ConnectTermReason','string');
$res->parseArray($rawData);
$res->setDatasetSize($numResults);
......@@ -912,6 +1026,8 @@
$res = removeWiSPLocationMember($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -922,6 +1038,8 @@
$res = updateWiSPLocation($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -931,6 +1049,8 @@
$res = createWiSPLocation($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -940,6 +1060,8 @@
$res = removeWiSPLocation($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -977,6 +1099,8 @@
$res = updateAdminUser($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -986,6 +1110,8 @@
$res = createAdminUser($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -995,6 +1121,8 @@
$res = removeAdminUser($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -1034,6 +1162,8 @@
$res = updateAdminRealm($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -1043,6 +1173,8 @@
$res = createAdminRealm($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -1052,6 +1184,8 @@
$res = removeAdminRealm($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -1091,6 +1225,8 @@
$res = updateAdminGroup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -1100,6 +1236,8 @@
$res = createAdminGroup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......@@ -1109,6 +1247,8 @@
$res = removeAdminGroup($soapParams);
if (isset($res)) {
ajaxException($res);
} else {
jsonSuccess();
}
break;
......
awitef @ e81e0f67
Subproject commit e81e0f67579cbfda8090563f11cf9c5ccda453b2