diff --git a/packages/pirania/files/etc/config/pirania b/packages/pirania/files/etc/config/pirania index 147d1990b..d2d228038 100644 --- a/packages/pirania/files/etc/config/pirania +++ b/packages/pirania/files/etc/config/pirania @@ -1,7 +1,7 @@ config base_config 'base_config' option enabled '0' option prune_expired_for_days '30' - option portal_domain 'thisnode.info' + option portal_domain 'gateway.info' option url_auth '/portal/auth.html' option url_authenticated '/portal/authenticated.html' option url_info '/portal/info.html' diff --git a/packages/pirania/files/etc/init.d/pirania-dnsmasq b/packages/pirania/files/etc/init.d/pirania-dnsmasq index 25f83c2e8..6f310a49a 100755 --- a/packages/pirania/files/etc/init.d/pirania-dnsmasq +++ b/packages/pirania/files/etc/init.d/pirania-dnsmasq @@ -15,7 +15,7 @@ start_service() { --no-resolv \ --server=/$(uci get dhcp.@dnsmasq[0].domain)/$(uci get network.lm_net_br_lan_anygw_if.ipaddr) \ --addn-hosts=/var/hosts/shared-state-dnsmasq_hosts \ - --address=/thisnode.info/$(uci get network.lm_net_br_lan_anygw_if.ipaddr) \ + --address=/gateway.info/$(uci get network.lm_net_br_lan_anygw_if.ipaddr) \ --address=/\#/1.2.3.4 # respawn automatically if something died, be careful if you have an alternative process supervisor diff --git a/packages/pirania/files/etc/init.d/pirania-mesh-sync b/packages/pirania/files/etc/init.d/pirania-mesh-sync new file mode 100755 index 000000000..de674e68b --- /dev/null +++ b/packages/pirania/files/etc/init.d/pirania-mesh-sync @@ -0,0 +1,71 @@ +#!/bin/sh /etc/rc.common + +START=99 + +start() { + ( + TIMEOUT=60 + INTERVAL=2 + ELAPSED=0 + ntpd -q -p pool.ntp.org + while [ $ELAPSED -lt $TIMEOUT ]; do + WAN_IF=$(ifstatus wan | jsonfilter -e '@.l3_device') + DEFAULT_ROUTE=$(ip route | awk '/^default/ {print $3" "$5; exit}') + BR_LAN_IP=$(ip -4 -o addr show dev br-lan | awk '{print $4}' | cut -d/ -f1) + GATEWAY=$(echo "$DEFAULT_ROUTE" | awk '{print $1}') + DEV=$(echo "$DEFAULT_ROUTE" | awk '{print $2}') + WLAN_MESH_MAC=$(ip link show "$DEV" | awk '/link\/ether/ {print $2}') + echo "DEBUG: WAN_IF=$WAN_IF" + echo "DEBUG: DEFAULT_ROUTE=$DEFAULT_ROUTE" + echo "DEBUG: BR_LAN_IP=$BR_LAN_IP" + echo "DEBUG: GATEWAY=$GATEWAY" + echo "DEBUG: DEV=$DEV" + echo "DEBUG: WLAN_MESH_MAC=$WLAN_MESH_MAC" + if [ -n "$DEV" ] && [ -n "$BR_LAN_IP" ] && [ -n "$WLAN_MESH_MAC" ] && [ -n "$GATEWAY" ] && [ "$DEV" != "$WAN_IF" ]; then + echo "DEBUG: SECONDARY" + if ! /usr/bin/voucher show_authorized_macs | grep -iq "$WLAN_MESH_MAC" || ! /usr/bin/voucher show_authorized_ips | grep -iq "$BR_LAN_IP"; then + CODE=$(/usr/bin/voucher add '# whitelist-secondary-node' | awk '{print $4}') + /usr/bin/voucher activate "$CODE" "$WLAN_MESH_MAC" "$BR_LAN_IP" + fi + for f in /etc/pirania/hooks/db_change/*; do + [ -x "$f" ] && "$f" + done + uci set dhcp.pirania="hostrecord" + uci set dhcp.pirania.name="gateway.info" + uci set dhcp.pirania.ip="$GATEWAY" + uci commit dhcp + /etc/init.d/dnsmasq reload + exit 0 + elif [ -n "$DEV" ] && [ -n "$BR_LAN_IP" ] && [ "$DEV" = "$WAN_IF" ]; then + echo "DEBUG: PRIMARY" + if [ "$1" != "skip" ]; then + sleep 300 + fi + for f in /etc/pirania/vouchers/*.json; do + if grep -q '"name"[[:space:]]*:[[:space:]]*"# whitelist-secondary-node"' "$f"; then + MAC=$(grep -o '"mac"[[:space:]]*:[[:space:]]*"[^"]*"' "$f" | awk -F'"' '{print $4}') + IP=$(grep -o '"ip"[[:space:]]*:[[:space:]]*"[^"]*"' "$f" | awk -F'"' '{print $4}') + if [ -n "$MAC" ] && [ -n "$IP" ]; then + lua -e "require('read_for_access.read_for_access').authorize_mac('$MAC', '$IP')" + fi + fi + done + uci set dhcp.pirania="hostrecord" + uci set dhcp.pirania.name="gateway.info" + uci set dhcp.pirania.ip="$BR_LAN_IP" + uci commit dhcp + /etc/init.d/dnsmasq reload + exit 0 + fi + sleep $INTERVAL + ELAPSED=$((ELAPSED + INTERVAL)) + done + echo "DEBUG: VARIABLES NOT FOUND" + uci set dhcp.pirania="hostrecord" + uci set dhcp.pirania.name="gateway.info" + uci set dhcp.pirania.ip="$BR_LAN_IP" + uci commit dhcp + /etc/init.d/dnsmasq reload + exit 1 + ) & +} \ No newline at end of file diff --git a/packages/pirania/files/etc/pirania/portal.json b/packages/pirania/files/etc/pirania/portal.json index a30e2a2c4..7989e6aca 100644 --- a/packages/pirania/files/etc/pirania/portal.json +++ b/packages/pirania/files/etc/pirania/portal.json @@ -1,6 +1,6 @@ { "title": "Pirania", - "main_text": "In order to have access to Interet you must enter a valid voucher. In the meantime you can continue using the local network services.", + "main_text": "In order to have access to Internet you must enter a valid voucher. In the meantime you can continue using the local network services.", "logo": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAABrCAMAAACi04sXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTExIDc5LjE1ODMyNSwgMjAxNS8wOS8xMC0wMToxMDoyMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTUgTWFjaW50b3NoIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjc0MjEyOEY5NUEyOTExRTlBNTMwOTU0MjBCQTA4RDNCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjc0MjEyOEZBNUEyOTExRTlBNTMwOTU0MjBCQTA4RDNCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzQyMTI4Rjc1QTI5MTFFOUE1MzA5NTQyMEJBMDhEM0IiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzQyMTI4Rjg1QTI5MTFFOUE1MzA5NTQyMEJBMDhEM0IiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4ENYiSAAAAM1BMVEUQEBBA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", "background_color": "#f0e5de", "link_title": "", diff --git a/packages/pirania/files/etc/uci-defaults/91-mesh-sync-update b/packages/pirania/files/etc/uci-defaults/91-mesh-sync-update new file mode 100755 index 000000000..d46bd838c --- /dev/null +++ b/packages/pirania/files/etc/uci-defaults/91-mesh-sync-update @@ -0,0 +1,12 @@ +#!/bin/sh +/etc/init.d/pirania-mesh-sync enable +/etc/init.d/pirania-mesh-sync start + +unique_append() +{ + grep -qF "$1" "$2" || echo "$1" >> "$2" +} + +unique_append \ + '*/10 * * * * /etc/init.d/pirania-mesh-sync restart &> /dev/null' \ + /etc/crontabs/root \ No newline at end of file diff --git a/packages/pirania/files/usr/bin/captive-portal b/packages/pirania/files/usr/bin/captive-portal index 99d3adae7..ca9b89578 100755 --- a/packages/pirania/files/usr/bin/captive-portal +++ b/packages/pirania/files/usr/bin/captive-portal @@ -11,57 +11,51 @@ clean_tables () { } set_nftables () { - echo "Apply captive-portal rules" - # Detect wheter add or insert rules - #append_nft_rules=$(uci get pirania.base_config.append_nft_rules 2> /dev/null) - #if [ "$append_nft_rules" = "1" ] ; then - # op="add rule" - #else - # op="insert rule" - #fi - - # Create pirania tables - nft create table inet pirania - # Create default tables and chains - nft add table inet pirania - nft add chain inet pirania prerouting { type nat hook prerouting priority 0 \; } - nft add chain inet pirania input { type filter hook input priority 0 \; } - nft add chain inet pirania forward { type filter hook forward priority 0 \; } - - # Add mac-adress set - nft add set inet pirania pirania-auth-macs { type ether_addr\; } - - # Create ipv4 set on pirania table - nft add set inet pirania pirania-allowlist-ipv4 { type ipv4_addr \; flags interval \; comment \"allow ipv4 list\" \; } - # Create ipv6 set on pirania table - nft add set inet pirania pirania-allowlist-ipv6 { type ipv6_addr \; flags interval \; comment \"allow ipv6 list\" \; } - - # Only accept packets from interfaces defined in catch_bridged_interfaces - catch_interfaces=$(uci get pirania.base_config.catch_bridged_interfaces | sed 's/ /,/g') - - # stop processing the chain for authorized macs and allowed ips (so they are accepted) - nft add rule inet pirania prerouting ether saddr @pirania-auth-macs ct state new,established,related counter log prefix "ValidSMAC" accept - nft add rule inet pirania prerouting ip daddr @pirania-allowlist-ipv4 ct state new,established,related counter log prefix "ACCEPT-ipv4" accept - nft add rule inet pirania prerouting ip6 daddr @pirania-allowlist-ipv6 ct state new,established,related counter log prefix "ACCEPT-ipv6" accept - - # send DNS requests, that are not from valid ips or macs, to our own captive portal DNS at 59053 - nft add rule inet pirania prerouting meta l4proto udp udp dport 53 ether saddr != @pirania-auth-macs ct state new,established,related counter log prefix "SMACDNS" redirect to :59053 - # redirect packets with dest port 80 to port 59080 of this host (the captive portal page). - nft add rule inet pirania prerouting meta l4proto tcp tcp dport 80 ether saddr != @pirania-auth-macs ct state new,established,related counter log prefix "SMACHTTP" redirect to :59080 - - #nft add rule inet pirania prerouting meta l4proto tcp tcp dport 80 ip saddr @pirania-allowlist-ipv4 ct state new,established,related counter log prefix "IPv4HTTP" redirect to :59080 - #nft add rule inet pirania prerouting meta l4proto tcp tcp dport 80 ip6 saddr @pirania-allowlist-ipv6 ct state new,established,related counter log prefix "IPV6HTTP" redirect to :59080 - - #nft add rule inet pirania prerouting meta l4proto udp udp dport 53 ip saddr @pirania-allowlist-ipv4 ct state new,established,related counter redirect to :59053 - #nft add rule inet pirania prerouting meta l4proto udp udp dport 53 ip6 saddr @pirania-allowlist-ipv6 ct state new,established,related counter redirect to :59053 - - - # reject - - #nft add rule inet pirania prerouting drop - #nft add rule inet pirania forward meta mark 0x11/0x11 counter reject with tcp reset - #nft add rule inet pirania forward meta mark 0x11/0x11 counter reject + echo "Apply captive-portal rules" + + # Previous cleanup + nft delete table inet pirania 2>/dev/null + + # Create the main table + nft add table inet pirania + + # Chains + nft add chain inet pirania prerouting \{ type nat hook prerouting priority -100\; \} + nft add chain inet pirania forward \{ type filter hook forward priority 0\; policy accept\; \} + + # Sets + nft add set inet pirania pirania-auth-macs \{ type ether_addr\; \} + nft add set inet pirania pirania-auth-ips \{ type ipv4_addr\; \} + nft add set inet pirania pirania-allowlist-ipv4 \{ type ipv4_addr\; flags interval\; comment \"allow ipv4 list\"\; \} + nft add set inet pirania pirania-allowlist-ipv6 \{ type ipv6_addr\; flags interval\; comment \"allow ipv6 list\"\; \} + + # Allowlist + nft add rule inet pirania prerouting ip daddr @pirania-allowlist-ipv4 accept + nft add rule inet pirania prerouting ip6 daddr @pirania-allowlist-ipv6 accept + + # --- Forward rules --- + # Allow traffic only if both IP and MAC are authorized + nft add rule inet pirania forward ether saddr @pirania-auth-macs ip saddr @pirania-auth-ips accept + + # DNS always allowed for redirect + nft add rule inet pirania forward udp dport 53 accept + # --- NAT redirect for unauthorized traffic --- + # HTTP redirect + nft add rule inet pirania prerouting tcp dport 80 ip saddr != @pirania-auth-ips redirect to :59080 + nft add rule inet pirania prerouting tcp dport 80 ether saddr != @pirania-auth-macs redirect to :59080 + + # DNS redirect + nft add rule inet pirania prerouting udp dport 53 ip saddr != @pirania-auth-ips redirect to :59053 + nft add rule inet pirania prerouting udp dport 53 ether saddr != @pirania-auth-macs redirect to :59053 + + # HTTPS block + nft add rule inet pirania forward tcp dport 443 ip saddr != @pirania-auth-ips log prefix "BLOCK_HTTPS" drop + nft add rule inet pirania forward tcp dport 443 ether saddr != @pirania-auth-macs log prefix "BLOCK_HTTPS" drop + + + + echo "Captive-portal rules applied successfully" } update_ipsets () { @@ -69,11 +63,20 @@ update_ipsets () { # Create tables and sets echo "Updating captive-portal rules" + # Flush macs and ips auth lists to exclude invalidated addresses + nft flush set inet pirania pirania-auth-macs + nft flush set inet pirania pirania-auth-ips + # Add authorized MAC addresses for mac in $(pirania_authorized_macs) ; do nft add element inet pirania pirania-auth-macs {$mac} echo "Adicionando enderecos:" $mac done + # Add authorized IP addresses + for ip in $(pirania_authorized_ips) ; do + nft add element inet pirania pirania-auth-ips {$ip} + echo "Adicionando enderecos:" $ip + done # Update pirania-allowlist sets for ipv4 and ipv6 nft flush set inet pirania pirania-allowlist-ipv4 @@ -109,7 +112,7 @@ elif [ "$1" = "clean" ] || [ "$1" = "stop" ] ; then elif [ "$enabled" = "1" ]; then echo "Captive-portal already enabled, reloading rules" clean_tables -# set_nftables + set_nftables # required for "/etc/init.d/pirania start|restart|reload" if "uci get pirania.base_config.enabled" is "1" update_ipsets exit elif [ "$1" = "enabled" ]; then @@ -120,4 +123,4 @@ elif [ "$1" = "enabled" ]; then else echo "Pirania captive-portal is disabled. Try running captive-portal start" exit -fi \ No newline at end of file +fi diff --git a/packages/pirania/files/usr/bin/pirania_authorized_ips b/packages/pirania/files/usr/bin/pirania_authorized_ips new file mode 100755 index 000000000..361acb9b9 --- /dev/null +++ b/packages/pirania/files/usr/bin/pirania_authorized_ips @@ -0,0 +1,7 @@ +#!/usr/bin/lua + +local portal = require('portal.portal') + +for _, ip in pairs(portal.get_authorized_ips()) do + print(ip) +end diff --git a/packages/pirania/files/usr/bin/voucher b/packages/pirania/files/usr/bin/voucher index 5847ccb11..adfe1c667 100755 --- a/packages/pirania/files/usr/bin/voucher +++ b/packages/pirania/files/usr/bin/voucher @@ -14,8 +14,8 @@ captive_portal = {} vouchera.init() -captive_portal.activate = function(code, mac) - local res = vouchera.activate(code, mac) +captive_portal.activate = function(code, mac, ip) + local res = vouchera.activate(code, mac, ip) if res then print('Voucher activated!') return true @@ -97,6 +97,15 @@ captive_portal.show_authorized_macs = function() return true end +captive_portal.show_authorized_ips = function() + for _, voucher in pairs(vouchera.vouchers) do + if voucher.is_active() then + print(voucher.ip) + end + end + return true +end + --! if is main if debug.getinfo(2).name == nil then local arguments = { ... } diff --git a/packages/pirania/files/usr/lib/lua/portal/portal.lua b/packages/pirania/files/usr/lib/lua/portal/portal.lua index a40e70847..5c8996b40 100644 --- a/packages/pirania/files/usr/lib/lua/portal/portal.lua +++ b/packages/pirania/files/usr/lib/lua/portal/portal.lua @@ -60,11 +60,25 @@ function portal.get_authorized_macs() return auth_macs end +function portal.get_authorized_ips() + local auth_ips = {} + local with_vouchers = portal.get_config().with_vouchers + if with_vouchers then + local vouchera = require("voucher.vouchera") + vouchera.init() + auth_ips = vouchera.get_authorized_ips() + else + auth_ips = read_for_access.get_authorized_ips() + end + return auth_ips +end + function portal.update_captive_portal(daemonized) if daemonized then utils.execute_daemonized('captive-portal update') else - os.execute('captive-portal update') + -- redirects stdout and stderr to /dev/null to not trigger 502 Bad Gateway after voucher portal auth + os.execute('captive-portal update > /dev/null 2>&1') end end diff --git a/packages/pirania/files/usr/lib/lua/read_for_access/cgi_handlers.lua b/packages/pirania/files/usr/lib/lua/read_for_access/cgi_handlers.lua index 12abc7e97..d5734b580 100644 --- a/packages/pirania/files/usr/lib/lua/read_for_access/cgi_handlers.lua +++ b/packages/pirania/files/usr/lib/lua/read_for_access/cgi_handlers.lua @@ -12,7 +12,7 @@ function handlers.authorize_mac() return uci:get("pirania", "base_config", "url_auth") end local client_data = utils.getIpv4AndMac(os.getenv('REMOTE_ADDR')) - read_for_access.authorize_mac(client_data.mac) + read_for_access.authorize_mac(client_data.mac, client_data.ip) local params = utils.urldecode_params(os.getenv("QUERY_STRING")) local url_prev = utils.urldecode(params['prev']) local url_authenticated = uci:get("pirania", "base_config", "url_authenticated") diff --git a/packages/pirania/files/usr/lib/lua/read_for_access/read_for_access.lua b/packages/pirania/files/usr/lib/lua/read_for_access/read_for_access.lua index 1d3873704..dd5244012 100644 --- a/packages/pirania/files/usr/lib/lua/read_for_access/read_for_access.lua +++ b/packages/pirania/files/usr/lib/lua/read_for_access/read_for_access.lua @@ -16,33 +16,31 @@ function read_for_access.set_workdir(workdir) error("Can't configure workdir " .. workdir) end read_for_access.AUTH_MACS_FILE = workdir .. '/auth_macs' + read_for_access.AUTH_IPS_FILE = workdir .. '/auth_ips' end read_for_access.set_workdir('/tmp/pirania/read_for_access') -function read_for_access.authorize_mac(mac) +function read_for_access.authorize_mac(mac, ip) local uci = config.get_uci_cursor() - local found = false - if utils.file_exists(read_for_access.AUTH_MACS_FILE) then - for line in io.lines(read_for_access.AUTH_MACS_FILE) do - if line:match(mac) then - found = true - break - end - end - end local duration = uci:get("pirania", "read_for_access", "duration_m") local timestamp = uptime_s() + tonumber(duration) * 60 - if not found then - local ofile = io.open(read_for_access.AUTH_MACS_FILE, 'a') - ofile:write(mac .. ' ' .. timestamp .. '\n') - ofile:close() - else - local content = utils.read_file(read_for_access.AUTH_MACS_FILE) - content = content:gsub("(" .. mac .. ") %d+", "%1 " .. timestamp) - utils.write_file(read_for_access.AUTH_MACS_FILE, content) + local function update_or_append(file, key, timestamp) + if not utils.file_exists(file) or not io.open(file):read("*a"):match(key) then + local ofile = io.open(file, 'a') + ofile:write(key .. ' ' .. timestamp .. '\n') + ofile:close() + else + local content = utils.read_file(file) + content = content:gsub("(" .. key .. ") %d+", "%1 " .. timestamp) + utils.write_file(file, content) + end end - os.execute('/usr/bin/captive-portal update') + + update_or_append(read_for_access.AUTH_MACS_FILE, mac, timestamp) + update_or_append(read_for_access.AUTH_IPS_FILE, ip, timestamp) + -- redirects stdout and stderr to /dev/null to not trigger 502 Bad Gateway after read for access portal + os.execute('/usr/bin/captive-portal update > /dev/null 2>&1') end function read_for_access.get_authorized_macs() @@ -61,4 +59,20 @@ function read_for_access.get_authorized_macs() return result end +function read_for_access.get_authorized_ips() + local result = {} + local current_time = uptime_s() + if not utils.file_exists(read_for_access.AUTH_IPS_FILE) then + return result + end + for line in io.lines(read_for_access.AUTH_IPS_FILE) do + local words = {} + for w in line:gmatch("%S+") do table.insert(words, w) end + if tonumber(words[2]) > current_time then + table.insert(result, words[1]) + end + end + return result +end + return read_for_access diff --git a/packages/pirania/files/usr/lib/lua/voucher/cgi_handlers.lua b/packages/pirania/files/usr/lib/lua/voucher/cgi_handlers.lua index 6960a0084..0f4e53312 100644 --- a/packages/pirania/files/usr/lib/lua/voucher/cgi_handlers.lua +++ b/packages/pirania/files/usr/lib/lua/voucher/cgi_handlers.lua @@ -32,7 +32,7 @@ function handlers.preactivate_voucher() local prevUrl = params['prev'] --! if client does not have javascript then activate right away without going to the INFO portal if params['nojs'] == 'true' then - if vouchera.activate(code, client_data.mac) then + if vouchera.activate(code, client_data.mac, client_data.ip) then url = url_authenticated else url = url_fail @@ -72,7 +72,7 @@ function handlers.activate_voucher() local url = url_fail .. (prevUrl and '?prev=' .. prevUrl or '') if code and client_data.mac then - if vouchera.activate(code, client_data.mac) then + if vouchera.activate(code, client_data.mac, client_data.ip) then if prevUrl ~= nil then url = prevUrl else diff --git a/packages/pirania/files/usr/lib/lua/voucher/vouchera.lua b/packages/pirania/files/usr/lib/lua/voucher/vouchera.lua index 9fc45eb0b..ff4d47ee2 100644 --- a/packages/pirania/files/usr/lib/lua/voucher/vouchera.lua +++ b/packages/pirania/files/usr/lib/lua/voucher/vouchera.lua @@ -40,6 +40,11 @@ function voucher_init(obj) end voucher.code = obj.code + if type(obj.ip) == "string" and not obj.ip:match("^%d+%.%d+%.%d+%.%d+$") then + return nil, "invalid ip" + end + voucher.ip = obj.ip + if type(obj.mac) == "string" and #obj.mac ~= 17 then return nil, "invalid mac" end @@ -78,7 +83,7 @@ function voucher_init(obj) if v.expiration_date() then expiration = os.date("%c", v.expiration_date()) end - return(string.format('%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s', v.id, v.name, v.code, v.mac or 'xx:xx:xx:xx:xx:xx', + return(string.format('%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s', v.id, v.name, v.code, v.ip or 'xx.xx.xx.xx', v.mac or 'xx:xx:xx:xx:xx:xx', creation, v.duration_m or 'perm', expiration, v.mod_counter)) end @@ -232,11 +237,12 @@ function vouchera.invalidate(id) end --! Activate a voucher returning true or false depending on the status of the operation. -function vouchera.activate(code, mac) +function vouchera.activate(code, mac, ip) local voucher = vouchera.is_activable(code) if voucher then function _update(v) v.mac = mac + v.ip = ip v.activation_date = os.time() end modify_voucher_with_func(voucher.id, _update) @@ -335,6 +341,16 @@ function vouchera.get_authorized_macs() return auth_macs end +function vouchera.get_authorized_ips() + local auth_ips = {} + for _, voucher in pairs(vouchera.vouchers) do + if voucher.is_active() then + table.insert(auth_ips, voucher.ip) + end + end + return auth_ips +end + vouchera.voucher = voucher_init return vouchera diff --git a/packages/pirania/files/www/portal/js/int.js b/packages/pirania/files/www/portal/js/int.js index a673abf98..55468e60b 100644 --- a/packages/pirania/files/www/portal/js/int.js +++ b/packages/pirania/files/www/portal/js/int.js @@ -25,6 +25,8 @@ const int = { } } +// 'en' fallback if user language is not available +int[lang] = int[lang] || int['en']; Object.keys(int[lang]).map(text => { Array.from(document.getElementsByClassName(`int-${text}`)).map( element => { @@ -35,4 +37,4 @@ Object.keys(int[lang]).map(text => { } } ) -}) +}) \ No newline at end of file diff --git a/packages/pirania/files/www/portal/js/ubusFetch.js b/packages/pirania/files/www/portal/js/ubusFetch.js index 3199485cd..8058e058c 100644 --- a/packages/pirania/files/www/portal/js/ubusFetch.js +++ b/packages/pirania/files/www/portal/js/ubusFetch.js @@ -1,4 +1,4 @@ -const url = 'http://thisnode.info/ubus' +const url = 'http://gateway.info/ubus' let ubusError = false function parseJSON(response) { @@ -21,7 +21,7 @@ const ubusFetch = (call, action, params, session) => new Promise ((resolve, reje method: 'POST', body: JSON.stringify(form), headers: { - 'Access-Control-Allow-Origin': 'http://thisnode.info' + 'Access-Control-Allow-Origin': 'http://gateway.info' }, }) .then(parseJSON) diff --git a/packages/pirania/tests/test_cgi_handlers.lua b/packages/pirania/tests/test_cgi_handlers.lua index 2b08a7723..a7f2e9fa8 100644 --- a/packages/pirania/tests/test_cgi_handlers.lua +++ b/packages/pirania/tests/test_cgi_handlers.lua @@ -149,7 +149,8 @@ describe('read_for_access cgi_handler authorize_mac #readforaccess', function() it('calls authorize_mac with the mac from the arp table for the client IP', function() rfa_handlers.authorize_mac() assert.stub(read_for_access.authorize_mac).was_called_with( - 'AA:BB:CC:DD:EE:FF' + 'AA:BB:CC:DD:EE:FF', + '10.1.1.1' ) end) diff --git a/packages/pirania/tests/test_read_for_access.lua b/packages/pirania/tests/test_read_for_access.lua index 634a3a2a4..d30a10e19 100644 --- a/packages/pirania/tests/test_read_for_access.lua +++ b/packages/pirania/tests/test_read_for_access.lua @@ -12,31 +12,39 @@ describe('read_for_access tests #readforaccess', function() it('saves authorized macs with configurable duration', function() stub(os, 'execute', function() end) local duration_m = uci:get('pirania', 'read_for_access', 'duration_m') - read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF') + read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF', '10.1.1.1') local auth_macs = read_for_access.get_authorized_macs() assert.is.equal(1, utils.tableLength(auth_macs)) assert.is.equal('AA:BB:CC:DD:EE:FF', auth_macs[1]) + local auth_ips = read_for_access.get_authorized_ips() + assert.is.equal(1, utils.tableLength(auth_ips)) + assert.is.equal('10.1.1.1', auth_ips[1]) current_time_s = current_time_s + (duration_m * 60) + 1 auth_macs = read_for_access.get_authorized_macs() assert.is.equal(0, utils.tableLength(auth_macs)) + auth_ips = read_for_access.get_authorized_ips() + assert.is.equal(0, utils.tableLength(auth_ips)) end) it('calls captive-portal-update on authorize_mac', function() stub(os, 'execute', function() end) - read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF') - assert.stub(os.execute).was_called_with('/usr/bin/captive-portal update') + read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF', '10.1.1.1') + assert.stub(os.execute).was_called_with('/usr/bin/captive-portal update > /dev/null 2>&1') end) it('let us re-authorize a mac', function() stub(os, 'execute', function() end) local duration_m = uci:get('pirania', 'read_for_access', 'duration_m') - read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF') + read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF', '10.1.1.1') current_time_s = current_time_s + (duration_m * 60) + 1 - read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF') + read_for_access.authorize_mac('AA:BB:CC:DD:EE:FF', '10.1.1.1') local auth_macs = read_for_access.get_authorized_macs() assert.is.equal(1, utils.tableLength(auth_macs)) assert.is.equal('AA:BB:CC:DD:EE:FF', auth_macs[1]) + local auth_ips = read_for_access.get_authorized_ips() + assert.is.equal(1, utils.tableLength(auth_ips)) + assert.is.equal('10.1.1.1', auth_ips[1]) end) before_each('', function() diff --git a/packages/pirania/tests/test_redirect.lua b/packages/pirania/tests/test_redirect.lua index 678773799..e40798165 100644 --- a/packages/pirania/tests/test_redirect.lua +++ b/packages/pirania/tests/test_redirect.lua @@ -19,7 +19,7 @@ describe('Pirania redirect request handler #portalredirect', function() local url_auth = uci:get('pirania', 'base_config', 'url_auth') handle_request(FAKE_ENV) assert.stub(uhttpd.send).was_called_with( - 'Location: http://thisnode.info' .. url_auth .. + 'Location: http://gateway.info' .. url_auth .. '?prev=http%3A%2F%2Fdetectportal.firefox.com%2Fsuccess.txt' .. '\r\n' ) @@ -31,7 +31,7 @@ describe('Pirania redirect request handler #portalredirect', function() local url_portal = uci:get('pirania', 'read_for_access', 'url_portal') handle_request(FAKE_ENV) assert.stub(uhttpd.send).was_called_with( - 'Location: http://thisnode.info' .. url_portal .. + 'Location: http://gateway.info' .. url_portal .. '?prev=http%3A%2F%2Fdetectportal.firefox.com%2Fsuccess.txt' .. '\r\n' )