mod_feature_validity.pm 7.69 KB
Newer Older
1
# Validity support
Nigel Kukard's avatar
Nigel Kukard committed
2 3
# Copyright (C) 2007-2016, AllWorldIT
#
4 5 6 7
# 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.
Nigel Kukard's avatar
Nigel Kukard committed
8
#
9 10 11 12
# 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.
Nigel Kukard's avatar
Nigel Kukard committed
13
#
14 15 16 17
# 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.

18
package smradius::modules::features::mod_feature_validity;
19 20 21 22 23 24 25 26 27 28 29 30

use strict;
use warnings;

# Modules we need
use smradius::constants;
use smradius::logging;
use smradius::util;
use DateTime;
use Date::Parse;

# Exporter stuff
Nigel Kukard's avatar
Nigel Kukard committed
31 32
use base qw(Exporter);
our @EXPORT = qw(
33
);
Nigel Kukard's avatar
Nigel Kukard committed
34
our @EXPORT_OK = qw(
35 36 37 38 39 40 41
);


# Plugin info
our $pluginInfo = {
	Name => "User Validity Feature",
	Init => \&init,
Nigel Kukard's avatar
Nigel Kukard committed
42

43 44
	# Authentication hook
	'Feature_Post-Authentication_hook' => \&checkValidity,
45
	'Feature_Post-Accounting_hook' => \&checkValidity
46 47 48 49 50 51
};


# Some constants
my $VALID_FROM_KEY = 'SMRadius-Validity-ValidFrom';
my $VALID_TO_KEY = 'SMRadius-Validity-ValidTo';
52
my $VALID_WINDOW_KEY = 'SMRadius-Validity-ValidWindow';
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74


## @internal
# Initialize module
sub init
{
	my $server = shift;
}


## @checkValidity($server,$user,$packet)
# Check Validity based on date
#
# @param server Server object
# @param user User data
# @param packet Radius packet
#
# @return Result
sub checkValidity
{
	my ($server,$user,$packet) = @_;

75 76 77 78 79 80 81

	# 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)");

82 83
	$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] POST AUTH HOOK");
	
Nigel Kukard's avatar
Nigel Kukard committed
84
	my ($validFrom,$validTo,$validWindow);
85 86 87 88 89


	# Get validity start date 
	if (defined($user->{'Attributes'}->{$VALID_FROM_KEY})) {
		$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] '".$VALID_FROM_KEY."' is defined");
Nigel Kukard's avatar
Nigel Kukard committed
90 91
		# Operator: :=
		if (defined($user->{'Attributes'}->{$VALID_FROM_KEY}->{':='})) {
92
			# Is it formatted as a date?
Nigel Kukard's avatar
Nigel Kukard committed
93 94
			if ($user->{'Attributes'}->{$VALID_FROM_KEY}->{':='}->{'Value'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
				$validFrom = $user->{'Attributes'}->{$VALID_FROM_KEY}->{':='}->{'Value'};
95
			} else {
Nigel Kukard's avatar
Nigel Kukard committed
96
				$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_FROM_KEY}->{':='}->{'Value'}.
97 98 99 100
						"' 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'");
Nigel Kukard's avatar
Nigel Kukard committed
101 102
		} # if (defined($user->{'Attributes'}->{$VALID_FROM_KEY}->{':='})) {
	} # if (defined($user->{'Attributes'}->{$VALID_FROM_KEY})) {
103 104 105 106 107


	# Get validity end date 
	if (defined($user->{'Attributes'}->{$VALID_TO_KEY})) {
		$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] '".$VALID_TO_KEY."' is defined");
Nigel Kukard's avatar
Nigel Kukard committed
108 109
		# Operator: :=
		if (defined($user->{'Attributes'}->{$VALID_TO_KEY}->{':='})) {
110
			# Is it formatted as a date?
Nigel Kukard's avatar
Nigel Kukard committed
111 112
			if ($user->{'Attributes'}->{$VALID_TO_KEY}->{':='}->{'Value'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
				$validTo = $user->{'Attributes'}->{$VALID_TO_KEY}->{':='}->{'Value'};
113
			} else {
Nigel Kukard's avatar
Nigel Kukard committed
114
				$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] '".$user->{'Attributes'}->{$VALID_TO_KEY}->{':='}->{'Value'}.
115 116 117 118
						"' 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'");
Nigel Kukard's avatar
Nigel Kukard committed
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
		} # 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})) {

139 140 141 142 143 144 145 146 147 148


	# Now ...
	my $now = $user->{'_Internal'}->{'Timestamp-Unix'};


	# Do we have a begin date?
	if (defined($validFrom)) {

		# Convert string to datetime
149
		my $validFrom_unixtime = str2time($validFrom,$server->{'smradius'}->{'event_timezone'});
150
		if (!defined($validFrom_unixtime)) {
Nigel Kukard's avatar
Nigel Kukard committed
151
			$server->log(LOG_NOTICE,"[MOD_FEATURE_VALIDITY] Date conversion failed on '%s'",$validFrom);
152 153 154 155 156

		# 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');

Nigel Kukard's avatar
Nigel Kukard committed
157
			$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid start date: '%s', rejecting",$pretty_dt);
158 159 160
			# Date not within valid period, must be disconnected

			return MOD_RES_NACK;
Nigel Kukard's avatar
Nigel Kukard committed
161 162
		} # if (!defined($validFrom_unixtime)) {
	} # if (defined($validFrom)) {
163 164 165 166 167

	# Do we have an end date?
	if (defined($validTo)) {

		# Convert string to datetime
168
		my $validTo_unixtime = str2time($validTo,$server->{'smradius'}->{'event_timezone'});
169
		if (!defined($validTo_unixtime)) {
Nigel Kukard's avatar
Nigel Kukard committed
170
				$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Date conversion failed on '%s'",$validTo);
171 172 173 174

		# 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');
Nigel Kukard's avatar
Nigel Kukard committed
175
			$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid end date: '%s', rejecting",$pretty_dt);
176 177 178
			# Date not within valid period, must be disconnected

			return MOD_RES_NACK;
Nigel Kukard's avatar
Nigel Kukard committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
		} # 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');
Nigel Kukard's avatar
Nigel Kukard committed
195 196
						$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] Current date outside valid window end date: '%s', ".
								"rejecting",$pretty_dt);
Nigel Kukard's avatar
Nigel Kukard committed
197 198 199 200 201
						# Date not within valid window, must be disconnected
						return MOD_RES_NACK;
					}
				}
	
202
			} else {
Nigel Kukard's avatar
Nigel Kukard committed
203 204
				$server->log(LOG_DEBUG,"[MOD_FEATURE_VALIDITY] No users_data 'global/FirstLogin' found for user '%s'",
						$user->{'Username'});
Nigel Kukard's avatar
Nigel Kukard committed
205
			} # if (defined(my $res = $module->{'Users_data_get'}($server,$user,'global','FirstLogin'))) {
206
		} else {
Nigel Kukard's avatar
Nigel Kukard committed
207 208
			$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'});
Nigel Kukard's avatar
Nigel Kukard committed
209
		} # if (defined($user->{'_UserDB'}->{'Users_data_get'})) {
210 211
	}

212 213 214 215 216 217
	return MOD_RES_ACK;
}


1;
# vim: ts=4