2026-06-04 15:33:41 -04:00
import json
2026-06-02 00:47:03 -04:00
import os
2026-06-02 12:49:39 -04:00
from config_utils import collect_layout_tokens , CONFIGS_DIR
2026-06-05 21:28:05 -04:00
import license
PRO_LICENSE = license . is_pro ( )
2026-06-02 00:47:03 -04:00
RADIUS_LOG_MAX = 50
RADIUS_LOG_FILE = ' /var/log/freeradius/radius.log '
2026-06-02 12:49:39 -04:00
def radius_log_tail ( cfg ) :
2026-06-02 00:47:03 -04:00
try :
log_max_kb = cfg . get ( ' radius ' , { } ) . get ( ' general ' , { } ) . get ( ' log_max_kb ' , 1024 )
2026-06-03 00:45:04 -04:00
current = [ ]
try :
with open ( RADIUS_LOG_FILE ) as f :
current = f . readlines ( )
except FileNotFoundError :
pass
prev = [ ]
if len ( current ) < RADIUS_LOG_MAX :
try :
with open ( RADIUS_LOG_FILE + ' .1 ' ) as f :
prev = f . readlines ( )
except FileNotFoundError :
pass
need = max ( 0 , RADIUS_LOG_MAX - len ( current ) )
lines = ( prev [ - need : ] if need and prev else [ ] ) + current
2026-06-02 00:47:03 -04:00
if not lines :
return ' (log is empty) ' , ' '
2026-06-03 00:45:04 -04:00
log_dir = os . path . dirname ( RADIUS_LOG_FILE )
try :
size_kb = sum (
os . path . getsize ( os . path . join ( log_dir , f ) )
for f in os . listdir ( log_dir )
if os . path . isfile ( os . path . join ( log_dir , f ) )
) / 1024
except OSError :
size_kb = 0.0
tail = lines [ - RADIUS_LOG_MAX : ]
pct = min ( 100 , round ( size_kb / log_max_kb * 100 ) ) if log_max_kb else 0
note = ' (includes rotated log) ' if ( prev and need ) else ' '
left = f ' Showing { len ( tail ) } lines { note } '
right = f ' Total log size: { size_kb : .1f } KB ( { pct } % of max) '
2026-06-02 00:47:03 -04:00
summary = (
' <div id= " radius-log-summary " class= " text-muted " style= " display:flex;justify-content:space-between;margin-top:0.5em; " > '
f ' <span> { left } </span><span> { right } </span></div> '
)
return ' ' . join ( tail ) . strip ( ) , summary
except Exception :
return ' (error reading log) ' , ' '
def collect_tokens ( cfg ) :
2026-06-02 12:49:39 -04:00
tokens = collect_layout_tokens ( cfg )
2026-06-02 00:47:03 -04:00
try :
tokens [ ' RADIUS_SECRET ' ] = open ( f ' { CONFIGS_DIR } /.radius-secret ' ) . read ( ) . strip ( )
except OSError :
tokens [ ' RADIUS_SECRET ' ] = ' (Generation is pending - visit Actions to apply generation command) '
fr = cfg . get ( ' radius ' , { } )
fr_opts = fr . get ( ' options ' , { } )
fr_gen = fr . get ( ' general ' , { } )
2026-06-05 02:23:08 -04:00
tokens [ ' RADIUS_MAC_FORMAT ' ] = fr_opts . get ( ' mac_format ' , ' aabbccddeeff ' )
2026-06-06 00:51:30 -04:00
tokens [ ' RADIUS_AUTH_MODE ' ] = fr_opts . get ( ' auth_mode ' , ' mab ' )
tokens [ ' RADIUS_EAP_PROTOCOL ' ] = fr_opts . get ( ' eap_protocol ' , ' eap_peap ' )
tokens [ ' RADIUS_EAP_PROTOCOL_OPTIONS ' ] = json . dumps ( [
{ ' value ' : ' eap_peap ' , ' label ' : ' EAP-PEAP ' } ,
{ ' value ' : ' eap_ttls ' , ' label ' : ' EAP-TTLS ' } ,
{ ' value ' : ' eap_md5 ' , ' label ' : ' EAP-MD5 ' } ,
] )
2026-06-05 21:40:42 -04:00
pro_suffix = ' ' if PRO_LICENSE else ' (PRO REQUIRED) '
pro_disabled = not PRO_LICENSE
2026-06-05 21:28:05 -04:00
tokens [ ' RADIUS_AUTH_MODE_OPTIONS ' ] = json . dumps ( [
2026-06-05 21:40:42 -04:00
{ ' value ' : ' mab ' , ' label ' : ' MAC Authentication Bypass (MAB) ' } ,
{ ' value ' : ' eap_password ' , ' label ' : f ' 802.1X - Client Username/Password { pro_suffix } ' , ' disabled ' : pro_disabled } ,
{ ' value ' : ' eap_credential ' , ' label ' : f ' 802.1X - Client Certificate { pro_suffix } ' , ' disabled ' : pro_disabled } ,
2026-06-05 21:28:05 -04:00
] )
2026-06-05 02:23:08 -04:00
tokens [ ' RADIUS_APPLY_TO ' ] = fr_opts . get ( ' apply_to ' , ' all ' )
tokens [ ' RADIUS_AP_IPS ' ] = json . dumps ( fr_opts . get ( ' ap_ips ' , [ ] ) )
2026-06-05 13:11:26 -04:00
all_radius_clients = [ r for r in cfg . get ( ' dhcp_reservations ' , [ ] ) if r . get ( ' radius_client ' ) is True ]
n = len ( all_radius_clients )
if n > 0 :
tokens [ ' RADIUS_CLIENT_STATUS_TEXT ' ] = f " RADIUS will be disabled if there are no RADIUS Clients specified on the DHCP Reservations page. There are currently { n } RADIUS Client { ' s ' if n != 1 else ' ' } . RADIUS is enabled. "
else :
tokens [ ' RADIUS_CLIENT_STATUS_TEXT ' ] = " RADIUS will be disabled if there are no RADIUS Clients specified on the DHCP Reservations page. There are currently 0 RADIUS Clients. RADIUS is disabled. "
2026-06-05 02:23:08 -04:00
radius_client_reservations = [
2026-06-05 13:11:26 -04:00
r for r in all_radius_clients
if r . get ( ' ip ' ) and r . get ( ' ip ' ) not in ( ' ' , ' dynamic ' )
2026-06-05 02:23:08 -04:00
]
tokens [ ' RADIUS_AP_IPS_OPTIONS ' ] = json . dumps ( [
{ ' value ' : r [ ' ip ' ] , ' label ' : f " { r . get ( ' description ' , r [ ' ip ' ] ) } ( { r [ ' ip ' ] } ) " }
for r in radius_client_reservations
] )
2026-06-02 00:47:03 -04:00
tokens [ ' RADIUS_LOGGING ' ] = ' true ' if fr_gen . get ( ' logging ' , False ) else ' '
2026-06-03 01:32:13 -04:00
tokens [ ' RADIUS_LOGGING_HINT ' ] = ' Unchecking will clear logs. ' if fr_gen . get ( ' logging ' , False ) else ' '
2026-06-02 00:47:03 -04:00
tokens [ ' RADIUS_GEN_LOG_MAX_KB ' ] = str ( fr_gen . get ( ' log_max_kb ' , 1024 ) )
2026-06-04 15:33:41 -04:00
2026-06-06 01:01:43 -04:00
tokens [ ' RADIUS_TUNNELED_REPLY ' ] = ' true ' if fr_opts . get ( ' tunneled_reply ' , False ) else ' '
2026-06-04 15:33:41 -04:00
vlans = cfg . get ( ' vlans ' , [ ] )
default_vlan = next ( ( v [ ' name ' ] for v in vlans if v . get ( ' radius_default ' ) is True ) , ' ' )
vlan_options = [ { ' value ' : ' ' , ' label ' : ' None (reject unknown devices) ' } ]
vlan_options + = [
{ ' value ' : v [ ' name ' ] , ' label ' : f " { v [ ' name ' ] } (VLAN { v . get ( ' vlan_id ' , ' ? ' ) } ) " }
for v in vlans
]
tokens [ ' RADIUS_DEFAULT_VLAN ' ] = default_vlan
tokens [ ' RADIUS_DEFAULT_VLAN_OPTIONS ' ] = json . dumps ( vlan_options )
2026-06-02 12:49:39 -04:00
tokens [ ' RADIUS_LOG_TAIL ' ] , tokens [ ' RADIUS_LOG_SUMMARY ' ] = radius_log_tail ( cfg )
2026-06-02 00:47:03 -04:00
return tokens