import datetime import json import sqlite3 import time import config_utils import factory import settings PRO_LICENSE = settings.is_pro() USER_TYPE_LABELS = {0: 'Captive Portal', 1: '802.1X'} def _load_credentials(): try: conn = sqlite3.connect(config_utils.CREDENTIALS_DB) conn.row_factory = sqlite3.Row conn.execute(""" CREATE TABLE IF NOT EXISTS credentials ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE COLLATE NOCASE, password TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', user_type INTEGER NOT NULL, digest_type INTEGER NOT NULL, vlan TEXT NOT NULL DEFAULT '', enabled INTEGER NOT NULL DEFAULT 1, date_set INTEGER NOT NULL, session_seconds INTEGER NOT NULL DEFAULT 0, expires_seconds INTEGER NOT NULL DEFAULT 0 ) """) conn.execute(""" CREATE TABLE IF NOT EXISTS sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, ip TEXT NOT NULL, credential_id INTEGER REFERENCES credentials(id) ON DELETE CASCADE, started_at INTEGER NOT NULL, expires_at INTEGER, mac TEXT NOT NULL DEFAULT '' ) """) conn.commit() rows = conn.execute("SELECT * FROM credentials ORDER BY id").fetchall() conn.close() return rows except Exception: return [] def _format_expiration(date_set, expires_seconds): if not expires_seconds: return 'Never' expires_ts = (date_set or 0) + expires_seconds now = int(time.time()) if expires_ts <= now: return 'Expired' return datetime.datetime.fromtimestamp(expires_ts).strftime('%Y-%m-%d') def _format_session(session_seconds): if not session_seconds: return 'No limit' if session_seconds % 86400 == 0: n = session_seconds // 86400 return f"{n} day{'s' if n != 1 else ''}" hours = session_seconds / 3600 return f"{hours:g} h" def collect_tokens(cfg): tokens = config_utils.collect_layout_tokens(cfg) tokens['PRO_NOTE'] = ( '' if PRO_LICENSE else '
' 'Client Credentials is a Routlin Pro feature. ' 'Credentials can be viewed but not added or edited without a Pro license.
' ) tokens['ADD_CREDENTIAL_DISABLED'] = 'true' tokens['RADIUS_DEFAULT_SESSION_SECONDS_JS'] = str( cfg.get('radius', {}).get('options', {}).get('default_session_seconds', 0) or 0 ) tokens['RADIUS_DEFAULT_EXPIRATION_SECONDS_JS'] = str( cfg.get('radius', {}).get('options', {}).get('default_expiration_seconds', 0) or 0 ) tokens['TYPE_FILTER_OPTIONS'] = ( '' '' '' ) vlans = [v for v in cfg.get('vlans', []) if not v.get('is_vpn')] tokens['CRED_VLAN_FILTER_OPTIONS'] = ( '' + ''.join(f'' for v in vlans) ) tokens['VLAN_OPTIONS'] = json.dumps( [{'value': '', 'label': '-- Select VLAN --'}] + [{'value': v['name'], 'label': f"{v['name']} (VLAN {v['vlan_id']})"} for v in vlans] ) captive_vlans = [v for v in cfg.get('vlans', []) if v.get('restricted_vlan') == 'c'] tokens['CAPTIVE_VLAN_OPTIONS'] = json.dumps( [{'value': '', 'label': '-- Select VLAN --'}] + [ { 'value': v['name'], 'label': f"{v['name']} (VLAN {v['vlan_id']})", 'require_upw': v.get('captive_portal', {}).get('require_username_password', v.get('require_username_password', False)), 'default_session_seconds': v.get('captive_portal', {}).get('default_session_seconds', v.get('default_session_seconds', 0)), 'default_expiration_seconds': v.get('captive_portal', {}).get('default_expiration_seconds', v.get('default_expiration_seconds', 0)), } for v in captive_vlans ] ) raw_rows = _load_credentials() display_rows = [] for row in raw_rows: r = dict(row) r.pop('password', None) r['user_type_label'] = USER_TYPE_LABELS.get(r.get('user_type'), str(r.get('user_type', ''))) r['expires_label'] = _format_session(r.get('session_seconds', 0)) r['expiration_label'] = _format_expiration(r.get('date_set', 0), r.get('expires_seconds', 0)) display_rows.append(r) content = factory.load_json(f'{factory.PAGES_DIR}/clientcredentials/content.json') for table_item in factory.iter_table_items(content.get('items', [])): ds = table_item.get('datasource', '') data = display_rows if ds == 'sqlite:client_credentials' else [] tokens[factory.table_token_key(ds)] = factory.build_table(table_item, tokens, data) return tokens