From 410eb34d4a062bba41e32cb8f0f1af3ea9f4f764 Mon Sep 17 00:00:00 2001 From: Ibnu Maksum Date: Sat, 6 Jul 2024 22:49:02 +0700 Subject: [PATCH] Radius Rest API --- radius.php | 227 +++++++++++++++++++++++++++++++ system/autoload/Package.php | 15 +- system/devices/MikrotikPppoe.php | 29 ++-- system/devices/RadiusRest.php | 2 +- system/lan/indonesia.json | 3 +- system/updates.json | 5 + version.json | 2 +- 7 files changed, 265 insertions(+), 18 deletions(-) diff --git a/radius.php b/radius.php index e69de29b..585c1576 100644 --- a/radius.php +++ b/radius.php @@ -0,0 +1,227 @@ +where('code', $username)->find_one(); + } else { + $d = ORM::for_table('tbl_customers')->where('username', $username)->find_one(); + } + if ($d) { + header("HTTP/1.1 204 No Content"); + die(); + } else { + show_radius_result([ + "control:Auth-Type" => "Reject", + "reply:Reply-Message" => 'Login invalid......' + ], 401); + } + break; + case 'authorize': + $username = _req('username'); + $password = _req('password'); + $isVoucher = ($username == $password); + if (empty($username)) { + show_radius_result([ + "control:Auth-Type" => "Reject", + "reply:Reply-Message" => 'Login invalid......' + ], 401); + } + $tur = ORM::for_table('tbl_user_recharges')->where('username', $username)->find_one(); + if ($tur) { + if (!$isVoucher) { + $d = ORM::for_table('tbl_customers')->select('password')->where('username', $username)->find_one(); + if ($d['password'] != $password) { + show_radius_result(['Reply-Message' => 'Username or Password is wrong'], 401); + } + } + process_radiust_rest($tur, $code); + } else { + if ($isVoucher) { + $v = ORM::for_table('tbl_voucher')->where('code', $username)->where('routers', 'radius')->find_one(); + if ($v) { + if ($v['status'] == 0) { + // voucher activation + if (Package::rechargeUser(0, $v['routers'], $v1['id_plan'], "Voucher", $username)) { + $v->status = "1"; + $v->save(); + $tur = ORM::for_table('tbl_user_recharges')->where('username', $username)->find_one(); + if ($tur) { + process_radiust_rest($tur, $code); + } else { + show_radius_result(['Reply-Message' => 'Voucher activation failed'], 401); + } + } else { + show_radius_result(['Reply-Message' => 'Voucher activation failed.'], 401); + } + } else { + show_radius_result(['Reply-Message' => 'Voucher Expired...'], 401); + } + } else { + show_radius_result(['Reply-Message' => 'Voucher Expired..'], 401); + } + } else { + show_radius_result(['Reply-Message' => 'Internet Plan Expired..'], 401); + } + } + break; + case 'accounting': + $username = _req('username'); + if (empty($username)) { + die(); + } + header("HTTP/1.1 200 ok"); + $d = ORM::for_table('rad_acct') + ->where('username', $username) + ->where('macaddr', _post('macAddr')) + ->where('acctstatustype', _post('acctStatusType')) + ->findOne(); + if (!$d) { + $d = ORM::for_table('rad_acct')->create(); + } + $d->acctsessionid = _post('acctSessionId'); + $d->username = $username; + $d->nasipaddress = _post('nasip'); + $d->nasid = _post('nasid'); + $d->nasportid = _post('nasPortId'); + $d->nasporttype = _post('nasPortType'); + $d->framedipaddress = _post('framedIPAddress'); + $d->acctstatustype = _post('acctStatusType'); + $d->macaddr = _post('macAddr'); + $d->dateAdded = date('Y-m-d H:i:s'); + $d->save(); + break; + } + die(); +} catch (Throwable $e) { + Message::sendTelegram( + "Sistem Error.\n" . + $e->getMessage() . "\n" . + $e->getTraceAsString(), + $config['telegram_topik_error'] + ); +} catch (Exception $e) { + Message::sendTelegram( + "Sistem Error.\n" . + $e->getMessage() . "\n" . + $e->getTraceAsString(), + $config['telegram_topik_error'] + ); +} +show_radius_result(['Reply-Message' => 'Invalid Command'], 401); + +function process_radiust_rest($tur, $code) +{ + global $config; + $plan = ORM::for_table('tbl_plans')->where('id', $tur['plan_id'])->find_one(); + $bw = ORM::for_table("tbl_bandwidth")->find_one($plan['id_bw']); + if ($bw['rate_down_unit'] == 'Kbps') { + $unitdown = 'K'; + } else { + $unitdown = 'M'; + } + if ($bw['rate_up_unit'] == 'Kbps') { + $unitup = 'K'; + } else { + $unitup = 'M'; + } + $rate = $bw['rate_up'] . $unitup . "/" . $bw['rate_down'] . $unitdown; + $rates = explode('/', $rate); + + if (!empty(trim($bw['burst']))) { + $ratos = $rate . ' ' . $bw['burst']; + } else { + $ratos = $rates[0] . '/' . $rates[1]; + } + + $attrs = []; + $timeexp = strtotime($tur['expiration'] . ' ' . $tur['time']); + $attrs['reply:Reply-Message'] = 'success'; + $attrs['Simultaneous-Use'] = $plan['shared_users']; + $attrs['reply:Mikrotik-Wireless-Comment'] = $plan['name_plan'] . ' | ' . $tur['expiration'] . ' ' . $tur['time']; + + $attrs['reply:Ascend-Data-Rate'] = str_replace('M', '000000', str_replace('K', '000', $rates[1])); + $attrs['reply:Ascend-Xmit-Rate'] = str_replace('M', '000000', str_replace('K', '000', $rates[0])); + $attrs['reply:Mikrotik-Rate-Limit'] = $ratos; + $attrs['reply:WISPr-Bandwidth-Max-Up'] = str_replace('M', '000000', str_replace('K', '000', $rates[0])); + $attrs['reply:WISPr-Bandwidth-Max-Down'] = str_replace('M', '000000', str_replace('K', '000', $rates[1])); + $attrs['reply:expiration'] = date('d M Y H:i:s', $timeexp); + $attrs['reply:WISPr-Session-Terminate-Time'] = date('Y-m-d', $timeexp) . 'T' . date('H:i:sP', $timeexp); + + if ($plan['typebp'] == "Limited") { + if ($plan['limit_type'] == "Time_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] * 60 * 60; + else + $timelimit = $plan['time_limit'] * 60; + $attrs['reply:Max-All-Session'] = $timelimit; + $attrs['reply:Expire-After'] = $timelimit; + } else if ($plan['limit_type'] == "Data_Limit") { + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + $attrs['reply:Max-Data'] = $datalimit; + $attrs['reply:Mikrotik-Recv-Limit-Gigawords'] = $datalimit; + $attrs['reply:Mikrotik-Xmit-Limit-Gigawords'] = $datalimit; + } else if ($plan['limit_type'] == "Both_Limit") { + if ($plan['time_unit'] == 'Hrs') + $timelimit = $plan['time_limit'] * 60 * 60; + else + $timelimit = $plan['time_limit'] * 60; + if ($plan['data_unit'] == 'GB') + $datalimit = $plan['data_limit'] . "000000000"; + else + $datalimit = $plan['data_limit'] . "000000"; + $attrs['reply:Max-All-Session'] = $timelimit; + $attrs['reply:Max-Data'] = $datalimit; + $attrs['reply:Mikrotik-Recv-Limit-Gigawords'] = $datalimit; + $attrs['reply:Mikrotik-Xmit-Limit-Gigawords'] = $datalimit; + } + } + $result = array_merge([ + "control:Auth-Type" => "Accept", + "reply" => ["Reply-Message" => ['value' => 'success']] + ], $attrs); + show_radius_result($result, $code); +} + +function show_radius_result($array, $code = 200) +{ + if ($code == 401) { + header("HTTP/1.1 401 Unauthorized"); + } else if ($code == 200) { + header("HTTP/1.1 200 OK"); + } else if ($code == 204) { + header("HTTP/1.1 204 No Content"); + die(); + } + die(json_encode($array)); + die(); +} diff --git a/system/autoload/Package.php b/system/autoload/Package.php index a8acdf1f..6f561e59 100644 --- a/system/autoload/Package.php +++ b/system/autoload/Package.php @@ -26,16 +26,25 @@ class Package $time_only = date("H:i:s"); $time = date("H:i:s"); $inv = ""; + $isVoucher = false; if ($id_customer == '' or $router_name == '' or $plan_id == '') { return false; } + if($channel == 'Voucher' && $id_customer = 0){ + $isVoucher = true; + } - $c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one(); $p = ORM::for_table('tbl_plans')->where('id', $plan_id)->find_one(); - if ($c['status'] != 'Active') { - _alert(Lang::T('This account status') . ' : ' . Lang::T($c['status']), 'danger', ""); + if(!$isVoucher){ + $c = ORM::for_table('tbl_customers')->where('id', $id_customer)->find_one(); + if ($c['status'] != 'Active') { + _alert(Lang::T('This account status') . ' : ' . Lang::T($c['status']), 'danger', ""); + } + }else{ + $c['username'] = $channel; + $c['fullname'] = $gateway; } $add_cost = 0; diff --git a/system/devices/MikrotikPppoe.php b/system/devices/MikrotikPppoe.php index 6c311b3d..01010dfc 100644 --- a/system/devices/MikrotikPppoe.php +++ b/system/devices/MikrotikPppoe.php @@ -31,9 +31,23 @@ class MikrotikPppoe { $mikrotik = $this->info($plan['routers']); $client = $this->getClient($mikrotik['ip_address'], $mikrotik['username'], $mikrotik['password']); - $this->removePpoeUser($client, $customer['username']); - $this->removePpoeActive($client, $customer['username']); - $this->addPpoeUser($client, $plan, $customer); + //check if customer exists + $printRequest = new RouterOS\Request('/ppp/secret/print'); + $printRequest->setQuery(RouterOS\Query::where('name', $customer['username'])); + $cid = $client->sendSync($printRequest)->getProperty('.id'); + if (empty($cid)) { + //customer not exists, add it + $this->addPpoeUser($client, $plan, $customer); + }else{ + $setRequest = new RouterOS\Request('/ppp/secret/set'); + $setRequest->setArgument('numbers', $cid); + $setRequest->setArgument('profile', $plan['name_plan']); + $setRequest->setArgument('comment', $customer['fullname'] . ' | ' . $customer['email']); + $setRequest->setArgument('password', $customer['password']); + $client->sendSync($setRequest); + //disconnect then + $this->removePpoeActive($client, $customer['username']); + } } function remove_customer($customer, $plan) @@ -212,15 +226,6 @@ class MikrotikPppoe return $client->sendSync($printRequest)->getProperty('.id'); } - - function connect_customer($customer, $ip, $mac_address, $router_name) - { - } - - function disconnect_customer($customer, $router_name) - { - } - function info($name) { return ORM::for_table('tbl_routers')->where('name', $name)->find_one(); diff --git a/system/devices/RadiusRest.php b/system/devices/RadiusRest.php index 78f9a971..a8ecb7d6 100644 --- a/system/devices/RadiusRest.php +++ b/system/devices/RadiusRest.php @@ -10,7 +10,7 @@ class RadiusRest { 'description' => 'This devices will handle Radius Connection using Rest API', 'author' => 'ibnu maksum', 'url' => [ - 'Wiki/Tutorial' => 'https://github.com/hotspotbilling/phpnuxbill/wiki/Radius-Rest', + 'Wiki Tutorial' => 'https://github.com/hotspotbilling/phpnuxbill/wiki/FreeRadius-Rest', 'Telegram' => 'https://t.me/ibnux', 'Donate' => 'https://paypal.me/ibnux' ] diff --git a/system/lan/indonesia.json b/system/lan/indonesia.json index 61a5fa43..ea4689a7 100644 --- a/system/lan/indonesia.json +++ b/system/lan/indonesia.json @@ -561,5 +561,6 @@ "Please_wait_1015_seconds_before_sending_another_SMS": "Harap tunggu 1015 detik sebelum mengirim SMS lainnya", "Phone_number_updated_successfully": "Nomor telepon berhasil diperbarui", "You_cannot_use_your_current_phone_number": "Anda tidak dapat menggunakan nomor telepon Anda saat ini", - "Devices": "Perangkat" + "Devices": "Perangkat", + "Voucher_Prefix": "Awalan Voucher" } \ No newline at end of file diff --git a/system/updates.json b/system/updates.json index a0aadee5..618fac29 100644 --- a/system/updates.json +++ b/system/updates.json @@ -120,5 +120,10 @@ "2024.6.21" : [ "ALTER TABLE `tbl_plans` ADD `on_login` TEXT NULL DEFAULT NULL AFTER `device`;", "ALTER TABLE `tbl_plans` ADD `on_logout` TEXT NULL DEFAULT NULL AFTER `on_login`;" + ], + "2024.7.6" : [ + "CREATE TABLE IF NOT EXISTS `rad_acct` ( `id` bigint NOT NULL, `acctsessionid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasid` varchar(32) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '', `nasportid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `nasporttype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `framedipaddress` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',`acctstatustype` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL, `macaddr` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, `dateAdded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;", + "ALTER TABLE `rad_acct` ADD PRIMARY KEY (`id`), ADD KEY `username` (`username`), ADD KEY `framedipaddress` (`framedipaddress`), ADD KEY `acctsessionid` (`acctsessionid`), ADD KEY `nasipaddress` (`nasipaddress`);", + "ALTER TABLE `rad_acct` MODIFY `id` bigint NOT NULL AUTO_INCREMENT;" ] } \ No newline at end of file diff --git a/version.json b/version.json index d8a2a3b9..d28e54fc 100644 --- a/version.json +++ b/version.json @@ -1,3 +1,3 @@ { - "version": "2024.6.25" + "version": "2024.7.6" } \ No newline at end of file