mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 19:33:11 +00:00
Merge branch '219-handle-juniper-for-autocapture' into 'dev'
Resolve "Handle Juniper for autocapture" See merge request federez/re2o!436
This commit is contained in:
commit
40258bfd76
15 changed files with 779 additions and 37 deletions
23
apt_requirements_radius.txt
Normal file
23
apt_requirements_radius.txt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
python-django
|
||||||
|
python-dateutil
|
||||||
|
texlive-latex-base
|
||||||
|
texlive-fonts-recommended
|
||||||
|
python-djangorestframework
|
||||||
|
python-django-reversion
|
||||||
|
python-pip
|
||||||
|
libsasl2-dev libldap2-dev
|
||||||
|
libssl-dev
|
||||||
|
python-crypto
|
||||||
|
python-git
|
||||||
|
javascript-common
|
||||||
|
libjs-jquery
|
||||||
|
libjs-jquery-ui
|
||||||
|
libjs-jquery-timepicker
|
||||||
|
libjs-bootstrap
|
||||||
|
fonts-font-awesome
|
||||||
|
graphviz
|
||||||
|
git
|
||||||
|
gettext
|
||||||
|
freeradius-common
|
||||||
|
freeradius-python2
|
||||||
|
python-mysqldb
|
|
@ -117,8 +117,11 @@ def radius_event(fun):
|
||||||
# (str,str) : rlm_python ne digère PAS les unicodes
|
# (str,str) : rlm_python ne digère PAS les unicodes
|
||||||
return fun(data)
|
return fun(data)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
|
exc_type, exc_instance, exc_traceback = sys.exc_info()
|
||||||
|
formatted_traceback = ''.join(traceback.format_tb(
|
||||||
|
exc_traceback))
|
||||||
logger.error('Failed %r on data %r' % (err, auth_data))
|
logger.error('Failed %r on data %r' % (err, auth_data))
|
||||||
logger.debug('Function %r, Traceback: %s' % (fun, repr(traceback.format_stack())))
|
logger.error('Function %r, Traceback : %r' % (fun, formatted_traceback))
|
||||||
return radiusd.RLM_MODULE_FAIL
|
return radiusd.RLM_MODULE_FAIL
|
||||||
|
|
||||||
return new_f
|
return new_f
|
||||||
|
@ -225,7 +228,7 @@ def post_auth(data):
|
||||||
# La ligne suivante fonctionne pour cisco, HP et Juniper
|
# La ligne suivante fonctionne pour cisco, HP et Juniper
|
||||||
port = port.split(".")[0].split('/')[-1][-2:]
|
port = port.split(".")[0].split('/')[-1][-2:]
|
||||||
out = decide_vlan_switch(nas_machine, nas_type, port, mac)
|
out = decide_vlan_switch(nas_machine, nas_type, port, mac)
|
||||||
sw_name, room, reason, vlan_id, decision = out
|
sw_name, room, reason, vlan_id, decision, attributes = out
|
||||||
|
|
||||||
if decision:
|
if decision:
|
||||||
log_message = '(fil) %s -> %s [%s%s]' % (
|
log_message = '(fil) %s -> %s [%s%s]' % (
|
||||||
|
@ -243,7 +246,7 @@ def post_auth(data):
|
||||||
("Tunnel-Type", "VLAN"),
|
("Tunnel-Type", "VLAN"),
|
||||||
("Tunnel-Medium-Type", "IEEE-802"),
|
("Tunnel-Medium-Type", "IEEE-802"),
|
||||||
("Tunnel-Private-Group-Id", '%d' % int(vlan_id)),
|
("Tunnel-Private-Group-Id", '%d' % int(vlan_id)),
|
||||||
),
|
) + tuple(attributes),
|
||||||
()
|
()
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -254,7 +257,11 @@ def post_auth(data):
|
||||||
)
|
)
|
||||||
logger.info(log_message)
|
logger.info(log_message)
|
||||||
|
|
||||||
return radiusd.RLM_MODULE_REJECT
|
return (
|
||||||
|
radiusd.RLM_MODULE_REJECT,
|
||||||
|
tuple(attributes),
|
||||||
|
()
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return radiusd.RLM_MODULE_OK
|
return radiusd.RLM_MODULE_OK
|
||||||
|
@ -363,18 +370,32 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
- raison de la décision (str)
|
- raison de la décision (str)
|
||||||
- vlan_id (int)
|
- vlan_id (int)
|
||||||
- decision (bool)
|
- decision (bool)
|
||||||
|
- Attributs supplémentaires (attribut:str, operateur:str, valeur:str)
|
||||||
"""
|
"""
|
||||||
|
attributes_kwargs = {
|
||||||
|
'client_mac' : str(mac_address),
|
||||||
|
'switch_port' : str(port_number),
|
||||||
|
}
|
||||||
# Get port from switch and port number
|
# Get port from switch and port number
|
||||||
extra_log = ""
|
extra_log = ""
|
||||||
# Si le NAS est inconnu, on place sur le vlan defaut
|
# Si le NAS est inconnu, on place sur le vlan defaut
|
||||||
if not nas_machine:
|
if not nas_machine:
|
||||||
return ('?', u'Chambre inconnue', u'Nas inconnu', RadiusOption.get_cached_value('vlan_decision_ok').vlan_id, True)
|
return (
|
||||||
|
'?',
|
||||||
|
u'Chambre inconnue',
|
||||||
|
u'Nas inconnu',
|
||||||
|
RadiusOption.get_cached_value('vlan_decision_ok').vlan_id,
|
||||||
|
True,
|
||||||
|
RadiusOption.get_attributes('ok_attributes', attributes_kwargs)
|
||||||
|
)
|
||||||
|
|
||||||
sw_name = str(getattr(nas_machine, 'short_name', str(nas_machine)))
|
sw_name = str(getattr(nas_machine, 'short_name', str(nas_machine)))
|
||||||
|
|
||||||
|
switch = Switch.objects.filter(machine_ptr=nas_machine).first()
|
||||||
|
attributes_kwargs['switch_ip'] = str(switch.ipv4)
|
||||||
port = (Port.objects
|
port = (Port.objects
|
||||||
.filter(
|
.filter(
|
||||||
switch=Switch.objects.filter(machine_ptr=nas_machine),
|
switch=switch,
|
||||||
port=port_number
|
port=port_number
|
||||||
)
|
)
|
||||||
.first())
|
.first())
|
||||||
|
@ -385,10 +406,11 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
if not port:
|
if not port:
|
||||||
return (
|
return (
|
||||||
sw_name,
|
sw_name,
|
||||||
"Chambre inconnue",
|
"Port inconnu",
|
||||||
u'Port inconnu',
|
u'Port inconnu',
|
||||||
getattr(RadiusOption.get_cached_value('unknown_port_vlan'), 'vlan_id', None),
|
getattr(RadiusOption.get_cached_value('unknown_port_vlan'), 'vlan_id', None),
|
||||||
RadiusOption.get_cached_value('unknown_port')!= RadiusOption.REJECT
|
RadiusOption.get_cached_value('unknown_port')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('unknown_port_attributes', attributes_kwargs)
|
||||||
)
|
)
|
||||||
|
|
||||||
# On récupère le profil du port
|
# On récupère le profil du port
|
||||||
|
@ -399,12 +421,14 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
if port_profile.vlan_untagged:
|
if port_profile.vlan_untagged:
|
||||||
DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id)
|
DECISION_VLAN = int(port_profile.vlan_untagged.vlan_id)
|
||||||
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
|
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
|
||||||
|
attributes = ()
|
||||||
else:
|
else:
|
||||||
DECISION_VLAN = RadiusOption.get_cached_value('vlan_decision_ok').vlan_id
|
DECISION_VLAN = RadiusOption.get_cached_value('vlan_decision_ok').vlan_id
|
||||||
|
attributes = RadiusOption.get_attributes('ok_attributes', attributes_kwargs)
|
||||||
|
|
||||||
# Si le port est désactivé, on rejette la connexion
|
# Si le port est désactivé, on rejette la connexion
|
||||||
if not port.state:
|
if not port.state:
|
||||||
return (sw_name, port.room, u'Port desactive', None, False)
|
return (sw_name, port.room, u'Port desactive', None, False, ())
|
||||||
|
|
||||||
# Si radius est désactivé, on laisse passer
|
# Si radius est désactivé, on laisse passer
|
||||||
if port_profile.radius_type == 'NO':
|
if port_profile.radius_type == 'NO':
|
||||||
|
@ -412,7 +436,9 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
"",
|
"",
|
||||||
u"Pas d'authentification sur ce port" + extra_log,
|
u"Pas d'authentification sur ce port" + extra_log,
|
||||||
DECISION_VLAN,
|
DECISION_VLAN,
|
||||||
True)
|
True,
|
||||||
|
attributes
|
||||||
|
)
|
||||||
|
|
||||||
# Si le 802.1X est activé sur ce port, cela veut dire que la personne a
|
# Si le 802.1X est activé sur ce port, cela veut dire que la personne a
|
||||||
# été accept précédemment
|
# été accept précédemment
|
||||||
|
@ -424,7 +450,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u'Acceptation authentification 802.1X',
|
u'Acceptation authentification 802.1X',
|
||||||
DECISION_VLAN,
|
DECISION_VLAN,
|
||||||
True
|
True,
|
||||||
|
attributes
|
||||||
)
|
)
|
||||||
|
|
||||||
# Sinon, cela veut dire qu'on fait de l'auth radius par mac
|
# Sinon, cela veut dire qu'on fait de l'auth radius par mac
|
||||||
|
@ -441,7 +468,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
"Inconnue",
|
"Inconnue",
|
||||||
u'Chambre inconnue',
|
u'Chambre inconnue',
|
||||||
getattr(RadiusOption.get_cached_value('unknown_room_vlan'), 'vlan_id', None),
|
getattr(RadiusOption.get_cached_value('unknown_room_vlan'), 'vlan_id', None),
|
||||||
RadiusOption.get_cached_value('unknown_room')!= RadiusOption.REJECT
|
RadiusOption.get_cached_value('unknown_room')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('unknown_room_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
|
|
||||||
room_user = User.objects.filter(
|
room_user = User.objects.filter(
|
||||||
|
@ -451,18 +479,20 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
return (
|
return (
|
||||||
sw_name,
|
sw_name,
|
||||||
room,
|
room,
|
||||||
u'Chambre non cotisante -> Web redirect',
|
u'Chambre non cotisante',
|
||||||
None,
|
getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None),
|
||||||
False
|
RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('non_member_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
for user in room_user:
|
for user in room_user:
|
||||||
if user.is_ban() or user.state != User.STATE_ACTIVE:
|
if user.is_ban() or user.state != User.STATE_ACTIVE:
|
||||||
return (
|
return (
|
||||||
sw_name,
|
sw_name,
|
||||||
room,
|
room,
|
||||||
u'Utilisateur banni ou desactive -> Web redirect',
|
u'Utilisateur banni ou desactive',
|
||||||
None,
|
getattr(RadiusOption.get_cached_value('banned_vlan'), 'vlan_id', None),
|
||||||
False
|
RadiusOption.get_cached_value('banned')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('banned_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
elif not (user.is_connected() or user.is_whitelisted()):
|
elif not (user.is_connected() or user.is_whitelisted()):
|
||||||
return (
|
return (
|
||||||
|
@ -470,7 +500,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u'Utilisateur non cotisant',
|
u'Utilisateur non cotisant',
|
||||||
getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None),
|
getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None),
|
||||||
RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT
|
RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('non_member_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
# else: user OK, on passe à la verif MAC
|
# else: user OK, on passe à la verif MAC
|
||||||
|
|
||||||
|
@ -491,9 +522,10 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
return (
|
return (
|
||||||
sw_name,
|
sw_name,
|
||||||
room,
|
room,
|
||||||
u'Machine Inconnue -> Web redirect',
|
u'Machine Inconnue',
|
||||||
None,
|
getattr(RadiusOption.get_cached_value('unknown_machine_vlan'), 'vlan_id', None),
|
||||||
False
|
RadiusOption.get_cached_value('unknown_machine')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('unknown_machine_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
# Sinon on bascule sur la politique définie dans les options
|
# Sinon on bascule sur la politique définie dans les options
|
||||||
# radius.
|
# radius.
|
||||||
|
@ -503,7 +535,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
"",
|
"",
|
||||||
u'Machine inconnue',
|
u'Machine inconnue',
|
||||||
getattr(RadiusOption.get_cached_value('unknown_machine_vlan'), 'vlan_id', None),
|
getattr(RadiusOption.get_cached_value('unknown_machine_vlan'), 'vlan_id', None),
|
||||||
RadiusOption.get_cached_value('unknown_machine')!= RadiusOption.REJECT
|
RadiusOption.get_cached_value('unknown_machine')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('unknown_machine_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
|
|
||||||
# L'interface a été trouvée, on vérifie qu'elle est active,
|
# L'interface a été trouvée, on vérifie qu'elle est active,
|
||||||
|
@ -518,7 +551,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u'Adherent banni',
|
u'Adherent banni',
|
||||||
getattr(RadiusOption.get_cached_value('banned_vlan'), 'vlan_id', None),
|
getattr(RadiusOption.get_cached_value('banned_vlan'), 'vlan_id', None),
|
||||||
RadiusOption.get_cached_value('banned')!= RadiusOption.REJECT
|
RadiusOption.get_cached_value('banned')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('banned_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
if not interface.is_active:
|
if not interface.is_active:
|
||||||
return (
|
return (
|
||||||
|
@ -526,7 +560,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u'Machine non active / adherent non cotisant',
|
u'Machine non active / adherent non cotisant',
|
||||||
getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None),
|
getattr(RadiusOption.get_cached_value('non_member_vlan'), 'vlan_id', None),
|
||||||
RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT
|
RadiusOption.get_cached_value('non_member')!= RadiusOption.REJECT,
|
||||||
|
RadiusOption.get_attributes('non_member_attributes', attributes_kwargs),
|
||||||
)
|
)
|
||||||
# Si on choisi de placer les machines sur le vlan
|
# Si on choisi de placer les machines sur le vlan
|
||||||
# correspondant à leur type :
|
# correspondant à leur type :
|
||||||
|
@ -539,7 +574,8 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u"Ok, Reassignation de l'ipv4" + extra_log,
|
u"Ok, Reassignation de l'ipv4" + extra_log,
|
||||||
DECISION_VLAN,
|
DECISION_VLAN,
|
||||||
True
|
True,
|
||||||
|
attributes
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return (
|
return (
|
||||||
|
@ -547,5 +583,6 @@ def decide_vlan_switch(nas_machine, nas_type, port_number,
|
||||||
room,
|
room,
|
||||||
u'Machine OK' + extra_log,
|
u'Machine OK' + extra_log,
|
||||||
DECISION_VLAN,
|
DECISION_VLAN,
|
||||||
True
|
True,
|
||||||
|
attributes
|
||||||
)
|
)
|
||||||
|
|
29
freeradius_utils/freeradius3/clients.conf
Normal file
29
freeradius_utils/freeradius3/clients.conf
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# Your client radius configuration below
|
||||||
|
client radius-filaire {
|
||||||
|
ipaddr =
|
||||||
|
netmask =
|
||||||
|
secret =
|
||||||
|
require_message_authenticator = no
|
||||||
|
nastype = other
|
||||||
|
virtual_server = radius-filaire
|
||||||
|
}
|
||||||
|
client radius-wifi {
|
||||||
|
ipaddr =
|
||||||
|
netmask =
|
||||||
|
secret =
|
||||||
|
require_message_authenticator = no
|
||||||
|
nastype = other
|
||||||
|
virtual_server = radius-wifi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parangon (federez)
|
||||||
|
client parangon {
|
||||||
|
ipaddr = 185.230.78.47
|
||||||
|
secret = please_ask_for_a_secret_to_federez_admin
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dodecagon (federez)
|
||||||
|
client dodecagon {
|
||||||
|
ipaddr = 163.172.48.168
|
||||||
|
secret = please_ask_for_a_secret_to_federez_admin
|
||||||
|
}
|
320
install_re2o.sh
320
install_re2o.sh
|
@ -4,11 +4,21 @@ SETTINGS_LOCAL_FILE='re2o/settings_local.py'
|
||||||
SETTINGS_EXAMPLE_FILE='re2o/settings_local.example.py'
|
SETTINGS_EXAMPLE_FILE='re2o/settings_local.example.py'
|
||||||
|
|
||||||
APT_REQ_FILE="apt_requirements.txt"
|
APT_REQ_FILE="apt_requirements.txt"
|
||||||
|
APT_RADIUS_REQ_FILE="apt_requirements_radius.txt"
|
||||||
PIP_REQ_FILE="pip_requirements.txt"
|
PIP_REQ_FILE="pip_requirements.txt"
|
||||||
|
PIP_RADIUS_REQ_FILE="pip_requirements_radius.txt"
|
||||||
|
|
||||||
LDIF_DB_FILE="install_utils/db.ldiff"
|
LDIF_DB_FILE="install_utils/db.ldiff"
|
||||||
LDIF_SCHEMA_FILE="install_utils/schema.ldiff"
|
LDIF_SCHEMA_FILE="install_utils/schema.ldiff"
|
||||||
|
|
||||||
|
FREERADIUS_CLIENTS="freeradius_utils/freeradius3/clients.conf"
|
||||||
|
FREERADIUS_AUTH="freeradius_utils/auth.py"
|
||||||
|
FREERADIUS_RADIUSD="freeradius_utils/freeradius3/radiusd.conf"
|
||||||
|
FREERADIUS_MOD_PYTHON="freeradius_utils/freeradius3/mods-enabled/python"
|
||||||
|
FREERADIUS_MOD_EAP="freeradius_utils/freeradius3/mods-enabled/eap"
|
||||||
|
FREERADIUS_SITE_DEFAULT="freeradius_utils/freeradius3/sites-enabled/default"
|
||||||
|
FREERADIUS_SITE_INNER_TUNNEL="freeradius_utils/freeradius3/sites-enabled/inner-tunnel"
|
||||||
|
EDITOR="nano"
|
||||||
|
|
||||||
VALUE= # global value used to return values by some functions
|
VALUE= # global value used to return values by some functions
|
||||||
|
|
||||||
|
@ -73,6 +83,44 @@ install_requirements() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
install_radius_requirements() {
|
||||||
|
### Usage: install_radius_requirements
|
||||||
|
#
|
||||||
|
# This function will install the required packages from APT repository
|
||||||
|
# and Pypi repository. Those packages are all required for Re2o to work
|
||||||
|
# properly.
|
||||||
|
###
|
||||||
|
|
||||||
|
echo "Setting up the required packages ..."
|
||||||
|
cat $APT_RADIUS_REQ_FILE | xargs apt-get -y install
|
||||||
|
python -m pip install -r $PIP_RADIUS_REQ_FILE
|
||||||
|
echo "Setting up the required packages: Done"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
configure_radius() {
|
||||||
|
### Usage: configure_radius
|
||||||
|
#
|
||||||
|
# This function configures freeradius.
|
||||||
|
###
|
||||||
|
echo "Configuring Freeradius ..."
|
||||||
|
|
||||||
|
cat $FREERADIUS_CLIENTS >> /etc/freeradius/3.0/clients.conf
|
||||||
|
ln -fs $(pwd)/$FREERADIUS_AUTH /etc/freeradius/3.0/auth.py
|
||||||
|
ln -fs $(pwd)/$FREERADIUS_RADIUSD /etc/freeradius/3.0/radiusd.conf
|
||||||
|
ln -fs $(pwd)/$FREERADIUS_MOD_PYTHON /etc/freeradius/3.0/mods-enabled/python
|
||||||
|
ln -fs $(pwd)/$FREERADIUS_MOD_EAP /etc/freeradius/3.0/mods-enabled/eap
|
||||||
|
ln -fs $(pwd)/$FREERADIUS_SITE_DEFAULT /etc/freeradius/3.0/sites-enabled/default
|
||||||
|
ln -fs $(pwd)/$FREERADIUS_SITE_INNER_TUNNEL /etc/freeradius/3.0/sites-enabled/inner-tunnel
|
||||||
|
_ask_value "Ready to edit clients.conf ?" "yes"
|
||||||
|
$EDITOR /etc/freeradius/3.0/clients.conf
|
||||||
|
|
||||||
|
|
||||||
|
echo "Configuring Freeradius: Done"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
install_database() {
|
install_database() {
|
||||||
### Usage: install_database <engine_type> <local_setup> <db_name> <username> <password>
|
### Usage: install_database <engine_type> <local_setup> <db_name> <username> <password>
|
||||||
|
@ -715,6 +763,265 @@ interactive_guide() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interactive_radius_guide() {
|
||||||
|
### Usage: interactive_radius_guide
|
||||||
|
#
|
||||||
|
# This function will guide through the automated setup of radius with
|
||||||
|
# Re2o by asking the user for some informations and some installation
|
||||||
|
# choices. It will then proceed to setup and configuration of the
|
||||||
|
# required tools according to the user choices.
|
||||||
|
###
|
||||||
|
|
||||||
|
echo "Re2o setup !"
|
||||||
|
echo "This tool will help you setup re2o radius. It is highly recommended to use a Debian clean server for this operation."
|
||||||
|
|
||||||
|
echo "Installing basic packages required for this script to work ..."
|
||||||
|
apt-get -y install sudo dialog
|
||||||
|
echo "Installing basic packages required for this script to work: Done"
|
||||||
|
|
||||||
|
# Common setup for the dialog prompts
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
HEIGHT=20
|
||||||
|
WIDTH=60
|
||||||
|
CHOICE_HEIGHT=4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#############
|
||||||
|
## Welcome ##
|
||||||
|
#############
|
||||||
|
|
||||||
|
BACKTITLE="Re2o setup"
|
||||||
|
|
||||||
|
# Welcome prompt
|
||||||
|
TITLE="Welcome"
|
||||||
|
MSGBOX="This tool will help you setup re2o. It is highly recommended to use a Debian clean server for this operation."
|
||||||
|
init="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --msgbox "$MSGBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
######################
|
||||||
|
## Database options ##
|
||||||
|
######################
|
||||||
|
|
||||||
|
BACKTITLE="Re2o setup - configuration of the database"
|
||||||
|
|
||||||
|
# Prompt for choosing the database engine
|
||||||
|
TITLE="Database engine"
|
||||||
|
MENU="Which engine should be used as the database ?"
|
||||||
|
OPTIONS=(1 "mysql"
|
||||||
|
2 "postgresql")
|
||||||
|
sql_bdd_type="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --menu "$MENU" \
|
||||||
|
$HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt for choosing the database location
|
||||||
|
TITLE="SQL location"
|
||||||
|
MENU="Where to install the SQL database ?
|
||||||
|
* 'Local' will setup everything automatically but is not recommended for production
|
||||||
|
* 'Remote' will ask you to manually perform some setup commands on the remote server"
|
||||||
|
OPTIONS=(1 "Local"
|
||||||
|
2 "Remote")
|
||||||
|
sql_is_local="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --menu "$MENU" \
|
||||||
|
$HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
if [ $sql_is_local == 2 ]; then
|
||||||
|
# Prompt to enter the remote database hostname
|
||||||
|
TITLE="SQL hostname"
|
||||||
|
INPUTBOX="The hostname of the remote SQL database"
|
||||||
|
sql_host="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt to enter the remote database name
|
||||||
|
TITLE="SQL database name"
|
||||||
|
INPUTBOX="The name of the remote SQL database"
|
||||||
|
sql_name="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt to enter the remote database username
|
||||||
|
TITLE="SQL username"
|
||||||
|
INPUTBOX="The username to access the remote SQL database"
|
||||||
|
sql_login="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
clear
|
||||||
|
else
|
||||||
|
# Use of default values for local setup
|
||||||
|
sql_name="re2o"
|
||||||
|
sql_login="re2o"
|
||||||
|
sql_host="localhost"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt to enter the database password
|
||||||
|
TITLE="SQL password"
|
||||||
|
INPUTBOX="The password to access the SQL database"
|
||||||
|
sql_password="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##################
|
||||||
|
## LDAP options ##
|
||||||
|
##################
|
||||||
|
|
||||||
|
BACKTITLE="Re2o setup - configuration of the LDAP"
|
||||||
|
|
||||||
|
# Prompt to choose the LDAP location
|
||||||
|
TITLE="LDAP location"
|
||||||
|
MENU="Where would you like to install the LDAP ?
|
||||||
|
* 'Local' will setup everything automatically but is not recommended for production
|
||||||
|
* 'Remote' will ask you to manually perform some setup commands on the remote server"
|
||||||
|
OPTIONS=(1 "Local"
|
||||||
|
2 "Remote")
|
||||||
|
ldap_is_local="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --menu "$MENU" \
|
||||||
|
$HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt to enter the LDAP domain extension
|
||||||
|
TITLE="Domain extension"
|
||||||
|
INPUTBOX="The local domain extension to use (e.g. 'example.net'). This is used in the LDAP configuration."
|
||||||
|
extension_locale="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Building the DN of the LDAP from the extension
|
||||||
|
IFS='.' read -a extension_locale_array <<< $extension_locale
|
||||||
|
for i in "${extension_locale_array[@]}"
|
||||||
|
do
|
||||||
|
ldap_dn+="dc=$i,"
|
||||||
|
done
|
||||||
|
ldap_dn="${ldap_dn::-1}"
|
||||||
|
|
||||||
|
if [ "$ldap_is_local" == 2 ]; then
|
||||||
|
# Prompt to enter the remote LDAP hostname
|
||||||
|
TITLE="LDAP hostname"
|
||||||
|
INPUTBOX="The hostname of the remote LDAP"
|
||||||
|
ldap_host="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt to choose if TLS should be activated or not for the LDAP
|
||||||
|
TITLE="TLS on LDAP"
|
||||||
|
MENU="Would you like to activate TLS for communicating with the remote LDAP ?"
|
||||||
|
OPTIONS=(1 "Yes"
|
||||||
|
2 "No")
|
||||||
|
ldap_tls="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --MENU "$MENU" \
|
||||||
|
$HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt to enter the admin's CN of the remote LDAP
|
||||||
|
TITLE="CN of amdin user"
|
||||||
|
INPUTBOX="The CN entry for the admin user of the remote LDAP"
|
||||||
|
ldap_cn="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
else
|
||||||
|
ldap_cn="cn=admin,"
|
||||||
|
ldap_cn+="$ldap_dn"
|
||||||
|
ldap_host="localhost"
|
||||||
|
ldap_tls=2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prompt to enter the LDAP password
|
||||||
|
TITLE="LDAP password"
|
||||||
|
INPUTBOX="The password to access the LDAP"
|
||||||
|
ldap_password="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$INPUTBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#########################
|
||||||
|
## Mail server options ##
|
||||||
|
#########################
|
||||||
|
|
||||||
|
BACKTITLE="Re2o setup - configuration of the mail server"
|
||||||
|
|
||||||
|
# Prompt to enter the hostname of the mail server
|
||||||
|
TITLE="Mail server hostname"
|
||||||
|
INPUTBOX="The hostname of the mail server to use"
|
||||||
|
email_host="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --inputbox "$TITLE" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
# Prompt to choose the port of the mail server
|
||||||
|
TITLE="Mail server port"
|
||||||
|
MENU="Which port (thus which protocol) to use to contact the mail server"
|
||||||
|
OPTIONS=(25 "SMTP"
|
||||||
|
465 "SMTPS"
|
||||||
|
587 "Submission")
|
||||||
|
email_port="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --menu "$MENU" \
|
||||||
|
$HEIGHT $WIDTH $CHOICE_HEIGHT "${OPTIONS[@]}" 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
###############################
|
||||||
|
## End of configuration step ##
|
||||||
|
###############################
|
||||||
|
|
||||||
|
BACKTITLE="Re2o setup"
|
||||||
|
|
||||||
|
# Prompt to inform the config setup is over
|
||||||
|
TITLE="End of configuration step"
|
||||||
|
MSGBOX="The configuration step is now finished. The script will now perform the following actions:
|
||||||
|
* Install the required packages
|
||||||
|
* Install and setup the requested database if 'local' has been selected
|
||||||
|
* Install and setup the ldap if 'local' has been selected
|
||||||
|
* Write a local version of 'settings_local.py' file with the previously given informations
|
||||||
|
* Apply the Django migrations for the project
|
||||||
|
* Install and setup freeradius"
|
||||||
|
end_config="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --msgbox "$MSGBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
|
||||||
|
clear
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
################################
|
||||||
|
## Perform the actual actions ##
|
||||||
|
################################
|
||||||
|
|
||||||
|
install_radius_requirements
|
||||||
|
|
||||||
|
configure_radius
|
||||||
|
|
||||||
|
install_database "$sql_bdd_type" "$sql_is_local" "$sql_name" "$sql_login" "$sql_password"
|
||||||
|
|
||||||
|
install_ldap "$ldap_is_local" "$ldap_password" "$ldap_dn"
|
||||||
|
|
||||||
|
|
||||||
|
write_settings_file "$sql_bdd_type" "$sql_host" "$sql_name" "$sql_login" "$sql_password" \
|
||||||
|
"$ldap_cn" "$ldap_tls" "$ldap_password" "$ldap_host" "$ldap_dn" \
|
||||||
|
"$email_host" "$email_port" "$extension_locale" "$url_server"
|
||||||
|
|
||||||
|
update_django
|
||||||
|
|
||||||
|
|
||||||
|
###########################
|
||||||
|
## End of the setup step ##
|
||||||
|
###########################
|
||||||
|
|
||||||
|
BACKTITLE="Re2o setup"
|
||||||
|
|
||||||
|
# Prompt to inform the installation process is over
|
||||||
|
TITLE="End of the setup"
|
||||||
|
MSGBOX="You can now use your RADIUS server."
|
||||||
|
end="$(dialog --clear --backtitle "$BACKTITLE" \
|
||||||
|
--title "$TITLE" --msgbox "$MSGBOX" \
|
||||||
|
$HEIGHT $WIDTH 2>&1 >/dev/tty)"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interactive_update_settings() {
|
interactive_update_settings() {
|
||||||
### Usage: interactvie_update_settings
|
### Usage: interactvie_update_settings
|
||||||
|
@ -763,9 +1070,13 @@ main_function() {
|
||||||
echo " * {help} ---------- Display this quick usage documentation"
|
echo " * {help} ---------- Display this quick usage documentation"
|
||||||
echo " * {setup} --------- Launch the full interactive guide to setup entirely"
|
echo " * {setup} --------- Launch the full interactive guide to setup entirely"
|
||||||
echo " re2o from scratch"
|
echo " re2o from scratch"
|
||||||
|
echo " * {setup-radius} -- Launch the full interactive guide to setup entirely"
|
||||||
|
echo " re2o radius from scratch"
|
||||||
echo " * {update} -------- Collect frontend statics, install the missing APT"
|
echo " * {update} -------- Collect frontend statics, install the missing APT"
|
||||||
echo " and pip packages, copy LaTeX templates files"
|
echo " and pip packages, copy LaTeX templates files"
|
||||||
echo " and apply the migrations to the DB"
|
echo " and apply the migrations to the DB"
|
||||||
|
echo " * {update-radius} - Update radius apt and pip packages and copy radius"
|
||||||
|
echo " configuration files to their proper location."
|
||||||
echo " * {update-django} - Apply Django migration and collect frontend statics"
|
echo " * {update-django} - Apply Django migration and collect frontend statics"
|
||||||
echo " * {copy-template-files} - Copy LaTeX templates files to media/templates"
|
echo " * {copy-template-files} - Copy LaTeX templates files to media/templates"
|
||||||
echo " * {update-packages} Install the missing APT and pip packages"
|
echo " * {update-packages} Install the missing APT and pip packages"
|
||||||
|
@ -797,12 +1108,21 @@ main_function() {
|
||||||
interactive_guide
|
interactive_guide
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
setup-radius )
|
||||||
|
interactive_radius_guide
|
||||||
|
;;
|
||||||
|
|
||||||
update )
|
update )
|
||||||
install_requirements
|
install_requirements
|
||||||
copy_templates_files
|
copy_templates_files
|
||||||
update_django
|
update_django
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
update-radius )
|
||||||
|
install_radius_requirements
|
||||||
|
configure_radius
|
||||||
|
;;
|
||||||
|
|
||||||
copy-templates-files )
|
copy-templates-files )
|
||||||
copy_templates_files
|
copy_templates_files
|
||||||
;;
|
;;
|
||||||
|
|
3
pip_requirements_radius.txt
Normal file
3
pip_requirements_radius.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
django-bootstrap3
|
||||||
|
django-macaddress
|
||||||
|
django-ldapdb==1.3.0
|
|
@ -44,7 +44,8 @@ from .models import (
|
||||||
SwitchManagementCred,
|
SwitchManagementCred,
|
||||||
RadiusOption,
|
RadiusOption,
|
||||||
CotisationsOption,
|
CotisationsOption,
|
||||||
DocumentTemplate
|
DocumentTemplate,
|
||||||
|
RadiusAttribute
|
||||||
)
|
)
|
||||||
from topologie.models import Switch
|
from topologie.models import Switch
|
||||||
|
|
||||||
|
@ -411,3 +412,33 @@ class DelDocumentTemplateForm(FormRevMixin, Form):
|
||||||
self.fields['document_templates'].queryset = instances
|
self.fields['document_templates'].queryset = instances
|
||||||
else:
|
else:
|
||||||
self.fields['document_templates'].queryset = Banque.objects.all()
|
self.fields['document_templates'].queryset = Banque.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class RadiusAttributeForm(ModelForm):
|
||||||
|
"""Edit and add RADIUS attributes."""
|
||||||
|
class Meta:
|
||||||
|
model = RadiusAttribute
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
|
super(RadiusAttributeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class DelRadiusAttributeForm(Form):
|
||||||
|
"""Delete RADIUS attributes"""
|
||||||
|
attributes = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=RadiusAttribute.objects.none(),
|
||||||
|
label=_("Current attributes"),
|
||||||
|
widget=forms.CheckboxSelectMultiple
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
instances = kwargs.pop('instances', None)
|
||||||
|
super(DelServiceForm, self).__init__(*args, **kwargs)
|
||||||
|
if instances:
|
||||||
|
self.fields['attributes'].queryset = instances
|
||||||
|
else:
|
||||||
|
self.fields['attributes'].queryset = Attributes.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
|
60
preferences/migrations/0062_auto_20190910_1909.py
Normal file
60
preferences/migrations/0062_auto_20190910_1909.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.23 on 2019-09-10 17:09
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import re2o.mixins
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0061_optionaluser_allow_archived_connexion'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RadiusAttribute',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('attribute', models.CharField(help_text='See http://freeradius.org/rfc/attributes.html', max_length=255, verbose_name='Attribute')),
|
||||||
|
('value', models.CharField(max_length=255, verbose_name='Value')),
|
||||||
|
('comment', models.TextField(blank=True, default='', help_text='Use this field to document this attribute.', verbose_name='Comment')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'RADIUS attribute',
|
||||||
|
'verbose_name_plural': 'RADIUS attributes',
|
||||||
|
},
|
||||||
|
bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='radiusoption',
|
||||||
|
name='banned_attributes',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Answer attributes for banned users.', related_name='banned_attribute', to='preferences.RadiusAttribute', verbose_name='Banned attributes.'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='radiusoption',
|
||||||
|
name='non_member_attributes',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Answer attributes for non members.', related_name='non_member_attribute', to='preferences.RadiusAttribute', verbose_name='Non member attributes.'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='radiusoption',
|
||||||
|
name='ok_attributes',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Answer attributes for accepted users.', related_name='ok_attribute', to='preferences.RadiusAttribute', verbose_name='Accepted users attributes.'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='radiusoption',
|
||||||
|
name='unknown_machine_attributes',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Answer attributes for unknown machines.', related_name='unknown_machine_attribute', to='preferences.RadiusAttribute', verbose_name='Unknown machines attributes.'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='radiusoption',
|
||||||
|
name='unknown_port_attributes',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Answer attributes for unknown ports.', related_name='unknown_port_attribute', to='preferences.RadiusAttribute', verbose_name='Unknown ports attributes.'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='radiusoption',
|
||||||
|
name='unknown_room_attributes',
|
||||||
|
field=models.ManyToManyField(blank=True, help_text='Answer attributes for unknown rooms.', related_name='unknown_room_attribute', to='preferences.RadiusAttribute', verbose_name='Unknown rooms attributes.'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -591,6 +591,32 @@ class MailMessageOption(AclMixin, models.Model):
|
||||||
verbose_name = _("email message options")
|
verbose_name = _("email message options")
|
||||||
|
|
||||||
|
|
||||||
|
class RadiusAttribute(RevMixin, AclMixin, models.Model):
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("RADIUS attribute")
|
||||||
|
verbose_name_plural = _("RADIUS attributes")
|
||||||
|
|
||||||
|
attribute = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("Attribute"),
|
||||||
|
help_text=_("See http://freeradius.org/rfc/attributes.html"),
|
||||||
|
)
|
||||||
|
value = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("Value")
|
||||||
|
)
|
||||||
|
comment = models.TextField(
|
||||||
|
verbose_name=_("Comment"),
|
||||||
|
help_text=_("Use this field to document this attribute."),
|
||||||
|
blank=True,
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ' '.join([self.attribute, self.operator, self.value])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RadiusOption(AclMixin, PreferencesModel):
|
class RadiusOption(AclMixin, PreferencesModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("RADIUS policy")
|
verbose_name = _("RADIUS policy")
|
||||||
|
@ -628,6 +654,13 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
verbose_name=_("Unknown machines VLAN"),
|
verbose_name=_("Unknown machines VLAN"),
|
||||||
help_text=_("VLAN for unknown machines if not rejected")
|
help_text=_("VLAN for unknown machines if not rejected")
|
||||||
)
|
)
|
||||||
|
unknown_machine_attributes = models.ManyToManyField(
|
||||||
|
RadiusAttribute,
|
||||||
|
related_name='unknown_machine_attribute',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Unknown machines attributes."),
|
||||||
|
help_text=_("Answer attributes for unknown machines."),
|
||||||
|
)
|
||||||
unknown_port = models.CharField(
|
unknown_port = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
choices=CHOICE_POLICY,
|
choices=CHOICE_POLICY,
|
||||||
|
@ -643,6 +676,13 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
verbose_name=_("Unknown ports VLAN"),
|
verbose_name=_("Unknown ports VLAN"),
|
||||||
help_text=_("VLAN for unknown ports if not rejected")
|
help_text=_("VLAN for unknown ports if not rejected")
|
||||||
)
|
)
|
||||||
|
unknown_port_attributes = models.ManyToManyField(
|
||||||
|
RadiusAttribute,
|
||||||
|
related_name='unknown_port_attribute',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Unknown ports attributes."),
|
||||||
|
help_text=_("Answer attributes for unknown ports."),
|
||||||
|
)
|
||||||
unknown_room = models.CharField(
|
unknown_room = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
choices=CHOICE_POLICY,
|
choices=CHOICE_POLICY,
|
||||||
|
@ -659,6 +699,13 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
verbose_name=_("Unknown rooms VLAN"),
|
verbose_name=_("Unknown rooms VLAN"),
|
||||||
help_text=_("VLAN for unknown rooms if not rejected")
|
help_text=_("VLAN for unknown rooms if not rejected")
|
||||||
)
|
)
|
||||||
|
unknown_room_attributes = models.ManyToManyField(
|
||||||
|
RadiusAttribute,
|
||||||
|
related_name='unknown_room_attribute',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Unknown rooms attributes."),
|
||||||
|
help_text=_("Answer attributes for unknown rooms."),
|
||||||
|
)
|
||||||
non_member = models.CharField(
|
non_member = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
choices=CHOICE_POLICY,
|
choices=CHOICE_POLICY,
|
||||||
|
@ -674,6 +721,13 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
verbose_name=_("Non members VLAN"),
|
verbose_name=_("Non members VLAN"),
|
||||||
help_text=_("VLAN for non members if not rejected")
|
help_text=_("VLAN for non members if not rejected")
|
||||||
)
|
)
|
||||||
|
non_member_attributes = models.ManyToManyField(
|
||||||
|
RadiusAttribute,
|
||||||
|
related_name='non_member_attribute',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Non member attributes."),
|
||||||
|
help_text=_("Answer attributes for non members."),
|
||||||
|
)
|
||||||
banned = models.CharField(
|
banned = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
choices=CHOICE_POLICY,
|
choices=CHOICE_POLICY,
|
||||||
|
@ -689,6 +743,13 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
verbose_name=_("Banned users VLAN"),
|
verbose_name=_("Banned users VLAN"),
|
||||||
help_text=_("VLAN for banned users if not rejected")
|
help_text=_("VLAN for banned users if not rejected")
|
||||||
)
|
)
|
||||||
|
banned_attributes = models.ManyToManyField(
|
||||||
|
RadiusAttribute,
|
||||||
|
related_name='banned_attribute',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Banned attributes."),
|
||||||
|
help_text=_("Answer attributes for banned users."),
|
||||||
|
)
|
||||||
vlan_decision_ok = models.OneToOneField(
|
vlan_decision_ok = models.OneToOneField(
|
||||||
'machines.Vlan',
|
'machines.Vlan',
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
|
@ -696,6 +757,23 @@ class RadiusOption(AclMixin, PreferencesModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True
|
null=True
|
||||||
)
|
)
|
||||||
|
ok_attributes = models.ManyToManyField(
|
||||||
|
RadiusAttribute,
|
||||||
|
related_name='ok_attribute',
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("Accepted users attributes."),
|
||||||
|
help_text=_("Answer attributes for accepted users."),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_attributes(cls, name, attribute_kwargs={}):
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
str(attribute.attribute),
|
||||||
|
str(attribute.value % attribute_kwargs)
|
||||||
|
)
|
||||||
|
for attribute in cls.get_cached_value(name).all()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def default_invoice():
|
def default_invoice():
|
||||||
|
|
50
preferences/templates/preferences/aff_radiusattributes.html
Normal file
50
preferences/templates/preferences/aff_radiusattributes.html
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2018 Hugo Levy-Falk
|
||||||
|
|
||||||
|
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.
|
||||||
|
{% endcomment %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load acl %}
|
||||||
|
{% load logs_extra %}
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Attribute" %}</th>
|
||||||
|
<th>{% trans "Comment" %}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for attribute in radius_attributes %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ attribute }}</td>
|
||||||
|
<td>{{ attribute.comment }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% can_edit attribute%}
|
||||||
|
{% include 'buttons/edit.html' with href='preferences:edit-radiusattribute' id=attribute.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
{% can_delete attribute %}
|
||||||
|
{% include 'buttons/suppr.html' with href='preferences:del-radiusattribute' id=attribute.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
{% history_button attribute %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
|
@ -32,6 +32,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "VLAN for machines accepted by RADIUS" %}</th>
|
<th>{% trans "VLAN for machines accepted by RADIUS" %}</th>
|
||||||
<td><span class="label label-success">{% blocktrans with vlan_decision_ok=radiusoptions.vlan_decision_ok %}VLAN {{ vlan_decision_ok }}{% endblocktrans %}</span></td>
|
<td><span class="label label-success">{% blocktrans with vlan_decision_ok=radiusoptions.vlan_decision_ok %}VLAN {{ vlan_decision_ok }}{% endblocktrans %}</span></td>
|
||||||
|
<td>{% trans "Attributes" %}
|
||||||
|
<ul>
|
||||||
|
{% for attribute in radiusoptions.ok_attributes.all %}
|
||||||
|
<li>{{attribute}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<hr/>
|
<hr/>
|
||||||
|
@ -40,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Situation" %}</th>
|
<th>{% trans "Situation" %}</th>
|
||||||
<th>{% trans "Behaviour" %}</th>
|
<th>{% trans "Behaviour" %}</th>
|
||||||
|
<th>{% trans "Attributes" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -51,6 +59,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<span class="label label-success">{% blocktrans with unknown_machine_vlan=radiusoptions.unknown_machine_vlan %}VLAN {{ unknown_machine_vlan }}{% endblocktrans %}</span>
|
<span class="label label-success">{% blocktrans with unknown_machine_vlan=radiusoptions.unknown_machine_vlan %}VLAN {{ unknown_machine_vlan }}{% endblocktrans %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
{% for attribute in radiusoptions.unknown_machine_attributes.all %}
|
||||||
|
<li>{{attribute}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Unknown port" %}</th>
|
<th>{% trans "Unknown port" %}</th>
|
||||||
|
@ -61,6 +76,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<span class="label label-success">{% blocktrans with unknown_port_vlan=radiusoptions.unknown_port_vlan %}VLAN {{ unknown_port_vlan }}{% endblocktrans %}</span>
|
<span class="label label-success">{% blocktrans with unknown_port_vlan=radiusoptions.unknown_port_vlan %}VLAN {{ unknown_port_vlan }}{% endblocktrans %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
{% for attribute in radiusoptions.unknown_port_attributes.all %}
|
||||||
|
<li>{{attribute}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Unknown room" %}</th>
|
<th>{% trans "Unknown room" %}</th>
|
||||||
|
@ -71,6 +93,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<span class="label label-success">{% blocktrans with unknown_room_vlan=radiusoptions.unknown_room_vlan %}VLAN {{ unknown_room_vlan }}{% endblocktrans %}</span>
|
<span class="label label-success">{% blocktrans with unknown_room_vlan=radiusoptions.unknown_room_vlan %}VLAN {{ unknown_room_vlan }}{% endblocktrans %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
{% for attribute in radiusoptions.unknown_room_attributes.all %}
|
||||||
|
<li>{{attribute}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Non member" %}</th>
|
<th>{% trans "Non member" %}</th>
|
||||||
|
@ -81,16 +110,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<span class="label label-success">{% blocktrans with non_member_vlan=radiusoptions.non_member_vlan %}VLAN {{ non_member_vlan }}{% endblocktrans %}</span>
|
<span class="label label-success">{% blocktrans with non_member_vlan=radiusoptions.non_member_vlan %}VLAN {{ non_member_vlan }}{% endblocktrans %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
{% for attribute in radiusoptions.non_member_attributes.all %}
|
||||||
|
<li>{{attribute}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Banned user" %}</th>
|
<th>{% trans "Banned user" %}</th>
|
||||||
<td>
|
<td>
|
||||||
{% if radiusoptions.unknown_port == 'REJECT' %}
|
{% if radiusoptions.banned == 'REJECT' %}
|
||||||
<span class="label label-danger">{% trans "Reject" %}</span>
|
<span class="label label-danger">{% trans "Reject" %}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="label label-success">{% blocktrans with banned_vlan=radiusoptions.banned_vlan %}VLAN {{ banned_vlan }}{% endblocktrans %}</span>
|
<span class="label label-success">{% blocktrans with banned_vlan=radiusoptions.banned_vlan %}VLAN {{ banned_vlan }}{% endblocktrans %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
{% for attribute in radiusoptions.banned_attributes.all %}
|
||||||
|
<li>{{attribute}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
|
@ -298,11 +298,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<h4 class="panel-title"><a><i class="fa fa-circle"></i> {% trans "RADIUS preferences" %}</h4></a>
|
<h4 class="panel-title"><a><i class="fa fa-circle"></i> {% trans "RADIUS preferences" %}</h4></a>
|
||||||
</div>
|
</div>
|
||||||
<div id="collapse_radius" class="panel-collapse panel-body collapse">
|
<div id="collapse_radius" class="panel-collapse panel-body collapse">
|
||||||
|
<h5>{% trans "RADIUS policies" %}</h5>
|
||||||
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'RadiusOption' %}">
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:edit-options' 'RadiusOption' %}">
|
||||||
<i class="fa fa-edit"></i>
|
<i class="fa fa-edit"></i>
|
||||||
{% trans "Edit" %}
|
{% trans "Edit" %}
|
||||||
</a>
|
</a>
|
||||||
{% include 'preferences/aff_radiusoptions.html' %}
|
{% include 'preferences/aff_radiusoptions.html' %}
|
||||||
|
<h5>{% trans "Available RADIUS attributes"%}</h5>
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'preferences:add-radiusattribute' %}"><i class="fa fa-plus"></i>{% trans " Add an attribute" %}</a>
|
||||||
|
{% include 'preferences/aff_radiusattributes.html' %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -126,5 +126,12 @@ urlpatterns = [
|
||||||
views.del_document_template,
|
views.del_document_template,
|
||||||
name='del-document-template'
|
name='del-document-template'
|
||||||
),
|
),
|
||||||
|
url(r'^add_radiusattribute/$', views.add_radiusattribute, name='add-radiusattribute'),
|
||||||
|
url(
|
||||||
|
r'^edit_radiusattribute/(?P<radiusattributeid>[0-9]+)$',
|
||||||
|
views.edit_radiusattribute,
|
||||||
|
name='edit-radiusattribute'
|
||||||
|
),
|
||||||
|
url(r'^del_radiusattribute/(?P<radiusattributeid>[0-9]+)$', views.del_radiusattribute, name='del-radiusattribute'),
|
||||||
url(r'^$', views.display_options, name='display-options'),
|
url(r'^$', views.display_options, name='display-options'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -52,7 +52,9 @@ from .forms import (
|
||||||
RadiusKeyForm,
|
RadiusKeyForm,
|
||||||
SwitchManagementCredForm,
|
SwitchManagementCredForm,
|
||||||
DocumentTemplateForm,
|
DocumentTemplateForm,
|
||||||
DelDocumentTemplateForm
|
DelDocumentTemplateForm,
|
||||||
|
RadiusAttributeForm,
|
||||||
|
DelRadiusAttributeForm
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
Service,
|
Service,
|
||||||
|
@ -69,7 +71,8 @@ from .models import (
|
||||||
SwitchManagementCred,
|
SwitchManagementCred,
|
||||||
RadiusOption,
|
RadiusOption,
|
||||||
CotisationsOption,
|
CotisationsOption,
|
||||||
DocumentTemplate
|
DocumentTemplate,
|
||||||
|
RadiusAttribute
|
||||||
)
|
)
|
||||||
from . import models
|
from . import models
|
||||||
from . import forms
|
from . import forms
|
||||||
|
@ -94,6 +97,7 @@ def display_options(request):
|
||||||
radiuskey_list = RadiusKey.objects.all()
|
radiuskey_list = RadiusKey.objects.all()
|
||||||
switchmanagementcred_list = SwitchManagementCred.objects.all()
|
switchmanagementcred_list = SwitchManagementCred.objects.all()
|
||||||
radiusoptions, _ = RadiusOption.objects.get_or_create()
|
radiusoptions, _ = RadiusOption.objects.get_or_create()
|
||||||
|
radius_attributes = RadiusAttribute.objects.all()
|
||||||
cotisationsoptions, _created = CotisationsOption.objects.get_or_create()
|
cotisationsoptions, _created = CotisationsOption.objects.get_or_create()
|
||||||
document_template_list = DocumentTemplate.objects.order_by('name')
|
document_template_list = DocumentTemplate.objects.order_by('name')
|
||||||
|
|
||||||
|
@ -114,6 +118,7 @@ def display_options(request):
|
||||||
'radiuskey_list' : radiuskey_list,
|
'radiuskey_list' : radiuskey_list,
|
||||||
'switchmanagementcred_list': switchmanagementcred_list,
|
'switchmanagementcred_list': switchmanagementcred_list,
|
||||||
'radiusoptions' : radiusoptions,
|
'radiusoptions' : radiusoptions,
|
||||||
|
'radius_attributes' : radius_attributes,
|
||||||
'cotisationsoptions': cotisationsoptions,
|
'cotisationsoptions': cotisationsoptions,
|
||||||
'optionnal_templates_list': optionnal_templates_list,
|
'optionnal_templates_list': optionnal_templates_list,
|
||||||
'document_template_list': document_template_list,
|
'document_template_list': document_template_list,
|
||||||
|
@ -502,3 +507,54 @@ def del_document_template(request, instances):
|
||||||
'action_name': _("Delete"),
|
'action_name': _("Delete"),
|
||||||
'title': _("Delete document template")
|
'title': _("Delete document template")
|
||||||
}, 'preferences/preferences.html', request)
|
}, 'preferences/preferences.html', request)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_create(RadiusAttribute)
|
||||||
|
def add_radiusattribute(request):
|
||||||
|
"""Create a RADIUS attribute."""
|
||||||
|
attribute = RadiusAttributeForm(request.POST or None)
|
||||||
|
if attribute.is_valid():
|
||||||
|
attribute.save()
|
||||||
|
messages.success(request, _("The attribute was added."))
|
||||||
|
return redirect(reverse('preferences:display-options'))
|
||||||
|
return form(
|
||||||
|
{'preferenceform': attribute, 'action_name': _("Add a RADIUS attribute")},
|
||||||
|
'preferences/preferences.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_edit(RadiusAttribute)
|
||||||
|
def edit_radiusattribute(request, radiusattribute_instance, **_kwargs):
|
||||||
|
"""Edit a RADIUS attribute."""
|
||||||
|
attribute = RadiusAttributeForm(
|
||||||
|
request.POST or None,
|
||||||
|
instance=radiusattribute_instance
|
||||||
|
)
|
||||||
|
if attribute.is_valid():
|
||||||
|
attribute.save()
|
||||||
|
messages.success(request, _("The attribute was edited."))
|
||||||
|
return redirect(reverse('preferences:display-options'))
|
||||||
|
return form(
|
||||||
|
{'preferenceform': attribute, 'action_name': _("Edit")},
|
||||||
|
'preferences/preferences.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_delete(RadiusAttribute)
|
||||||
|
def del_radiusattribute(request, radiusattribute_instance, **_kwargs):
|
||||||
|
"""Delete a RADIUS attribute."""
|
||||||
|
if request.method == "POST":
|
||||||
|
radiusattribute_instance.delete()
|
||||||
|
messages.success(request, _("The attribute was deleted."))
|
||||||
|
return redirect(reverse('preferences:display-options'))
|
||||||
|
return form(
|
||||||
|
{'objet': radiusattribute_instance, 'objet_name': 'attribute'},
|
||||||
|
'preferences/delete.html',
|
||||||
|
request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# coding: utf-8
|
||||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
# quelques clics.
|
# quelques clics.
|
||||||
|
|
|
@ -410,7 +410,7 @@ class Switch(AclMixin, Machine):
|
||||||
port_profile = profile_queryset.filter(on_dormitory=self.get_dormitory).first() or profile_queryset.first()
|
port_profile = profile_queryset.filter(on_dormitory=self.get_dormitory).first() or profile_queryset.first()
|
||||||
else:
|
else:
|
||||||
port_profile = profile_queryset.first()
|
port_profile = profile_queryset.first()
|
||||||
return port_profile or Switch.nothing_profile
|
return port_profile or Switch.nothing_profile()
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def default_uplink_profile(self):
|
def default_uplink_profile(self):
|
||||||
|
@ -730,7 +730,7 @@ class Port(AclMixin, RevMixin, models.Model):
|
||||||
elif self.room:
|
elif self.room:
|
||||||
return self.switch.default_room_profile
|
return self.switch.default_room_profile
|
||||||
else:
|
else:
|
||||||
return Switch.nothing_profile
|
return Switch.nothing_profile()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_instance(cls, portid, *_args, **kwargs):
|
def get_instance(cls, portid, *_args, **kwargs):
|
||||||
|
|
Loading…
Reference in a new issue