mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
uspot: refactor code
* add a common.uc class * add ucode ubus calls Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
137
feeds/ucentral/uspot/files/usr/share/uspot/common.uc
Normal file
137
feeds/ucentral/uspot/files/usr/share/uspot/common.uc
Normal file
@@ -0,0 +1,137 @@
|
||||
'use strict';
|
||||
|
||||
let ubus = require('ubus');
|
||||
let fs = require('fs');
|
||||
let uci = require('uci').cursor();
|
||||
let config = uci.get_all('uspot');
|
||||
|
||||
let file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/header.html' : '/usr/share/uspot/header', 'r');
|
||||
let header = file.read('all');
|
||||
file.close();
|
||||
|
||||
file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/footer.html' : '/usr/share/uspot/footer', 'r');
|
||||
let footer = file.read('all');
|
||||
file.close();
|
||||
|
||||
function PO(id, english) {
|
||||
return english;
|
||||
}
|
||||
|
||||
return {
|
||||
fs,
|
||||
rtnl: require('rtnl'),
|
||||
uam: require('uam'),
|
||||
uci,
|
||||
config,
|
||||
header,
|
||||
footer,
|
||||
|
||||
// wrapper for scraping external tools stdout
|
||||
fs_popen: function(cmd) {
|
||||
let stdout = fs.popen(cmd);
|
||||
if (!stdout)
|
||||
return null;
|
||||
|
||||
let reply = null;
|
||||
try {
|
||||
reply = json(stdout.read('all'));
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
stdout.close();
|
||||
return reply;
|
||||
},
|
||||
|
||||
// give a client access to the internet
|
||||
allow_client: function(ctx) {
|
||||
ctx.ubus.call('spotfilter', 'client_set', {
|
||||
"interface": "hotspot",
|
||||
"address": replace(ctx.mac, '-', ':'),
|
||||
"state": 1,
|
||||
"dns_state": 1,
|
||||
"accounting": [ "dl", "ul"],
|
||||
"data": {
|
||||
"connect": time()
|
||||
}
|
||||
});
|
||||
if (ctx.query_string.userurl)
|
||||
include('redir.uc', { redir_location: ctx.query_string.userurl });
|
||||
else
|
||||
include('allow.uc', ctx);
|
||||
},
|
||||
|
||||
// generate the default radius auth payload
|
||||
radius_init: function(ctx) {
|
||||
return {
|
||||
server: sprintf('%s:%s:%s', this.config.radius.auth_server, this.config.radius.auth_port, this.config.radius.auth_secret),
|
||||
acct_session: "0123456789",
|
||||
client_ip: ctx.env.REMOTE_ADDR,
|
||||
called_station: ctx.mac,
|
||||
calling_station: this.config.uam.nasmac,
|
||||
nas_ip: ctx.env.SERVER_ADDR,
|
||||
nas_id: this.config.uam.nasid
|
||||
};
|
||||
},
|
||||
|
||||
radius_call: function(ctx, payload) {
|
||||
let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w');
|
||||
cfg.write(payload);
|
||||
cfg.close();
|
||||
|
||||
return this.fs_popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json');
|
||||
},
|
||||
|
||||
handle_request: function(env) {
|
||||
let mac;
|
||||
let form_data = {};
|
||||
let query_string = {};
|
||||
let post_data = '';
|
||||
let ctx = { env, header: this.header, footer: this.footer, mac, form_data, post_data, query_string, config: this.config, PO };
|
||||
|
||||
// lookup the peers MAC
|
||||
let macs = this.rtnl.request(this.rtnl.const.RTM_GETNEIGH, this.rtnl.const.NLM_F_DUMP, { });
|
||||
for (let m in macs)
|
||||
if (m.dst == env.REMOTE_HOST)
|
||||
ctx.mac = replace(m.lladdr, ':', '-');
|
||||
|
||||
// if the MAC lookup failed, go to the error page
|
||||
if (!ctx.mac) {
|
||||
include('error.uc', ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check if a client is already connected
|
||||
ctx.ubus = ubus.connect();
|
||||
let connected = ctx.ubus.call('spotfilter', 'client_get', {
|
||||
'interface': 'hotspot',
|
||||
'address': ctx.mac
|
||||
});
|
||||
if (connected?.state) {
|
||||
include('connected.uc', ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// split QUERY_STRING
|
||||
if (env.QUERY_STRING)
|
||||
for (let chunk in split(env.QUERY_STRING, '&')) {
|
||||
let m = match(chunk, /^([^=]+)=(.*)$/);
|
||||
if (!m) continue;
|
||||
ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20));
|
||||
}
|
||||
|
||||
// recv POST data
|
||||
if (env.CONTENT_LENGTH > 0)
|
||||
for (let chunk = uhttpd.recv(64); chunk != null; chunk = uhttpd.recv(64))
|
||||
post_data += replace(chunk, /[^[:graph:]]/g, '.');
|
||||
|
||||
// split POST data into an array
|
||||
if (post_data)
|
||||
for (let chunk in split(post_data, '&')) {
|
||||
let var = split(chunk, '=');
|
||||
if (length(var) != 2)
|
||||
continue;
|
||||
ctx.form_data[var[0]] = var[1];
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
};
|
||||
@@ -2,104 +2,45 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let fs = require('fs');
|
||||
let rtnl = require('rtnl');
|
||||
|
||||
let file = fs.open('/usr/share/uspot/header', 'r');
|
||||
let header = file.read('all');
|
||||
file.close();
|
||||
|
||||
file = fs.open('/usr/share/uspot/footer', 'r');
|
||||
let footer = file.read('all');
|
||||
file.close();
|
||||
|
||||
let uci = require('uci').cursor();
|
||||
let config = uci.get_all('uspot');
|
||||
push(REQUIRE_SEARCH_PATH, "/usr/share/uspot/*.uc");
|
||||
|
||||
let portal = require('common');
|
||||
let uam = require('uam');
|
||||
|
||||
// give a client access to the internet
|
||||
function allow_client(ctx) {
|
||||
system('ubus call spotfilter client_set \'{ "interface": "hotspot", "address": "' + replace(ctx.mac, '-', ':') + '", "state": 1, "dns_state": 1}\'');
|
||||
if (ctx.query_string.userurl)
|
||||
include('uam.uc', { uam_location: ctx.query_string.userurl });
|
||||
else
|
||||
include('allow.uc', ctx);
|
||||
}
|
||||
|
||||
// log the client in via radius
|
||||
function auth_client(ctx) {
|
||||
let password;
|
||||
let payload = {
|
||||
server: sprintf('%s:%s:%s',config.radius.auth_server, config.radius.auth_port, config.radius.auth_secret),
|
||||
acct_session: "0123456789",
|
||||
client_ip: ctx.env.REMOTE_ADDR,
|
||||
called_station: ctx.mac,
|
||||
calling_station: config.uam.nasmac,
|
||||
nas_ip: ctx.env.SERVER_ADDR,
|
||||
nas_id: config.uam.nasid
|
||||
};
|
||||
let payload = portal.radius_init(ctx);
|
||||
|
||||
if (ctx.query_string.username && ctx.query_string.response) {
|
||||
let challenge = uam.md5(config.uam.challenge, ctx.mac);
|
||||
let challenge = uam.md5(portal.config.uam.challenge, ctx.mac);
|
||||
|
||||
payload.type = 'uam-chap-auth';
|
||||
payload.username = ctx.query_string.username;
|
||||
payload.chap_password = ctx.query_string.response;
|
||||
if (config.uam.secret)
|
||||
payload.chap_challenge = uam.chap_challenge(challenge, config.uam.uam_secret);
|
||||
if (portal.config.uam.secret)
|
||||
payload.chap_challenge = uam.chap_challenge(challenge, portal.config.uam.uam_secret);
|
||||
else
|
||||
payload.chap_challenge = challenge;
|
||||
} else if (ctx.query_string.username && ctx.query_string.password) {
|
||||
payload.type = 'uam-auth';
|
||||
payload.username = ctx.mac;
|
||||
payload.password = uam.password(uam.md5(config.uam.challenge, ctx.mac), ctx.query_string.password, config.uam.uam_secret);
|
||||
payload.password = uam.password(uam.md5(portal.config.uam.challenge, ctx.mac), ctx.query_string.password, portal.config.uam.uam_secret);
|
||||
}
|
||||
|
||||
let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w');
|
||||
cfg.write(payload);
|
||||
cfg.close();
|
||||
|
||||
let stdout = fs.popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json');
|
||||
let reply;
|
||||
if (!stdout) {
|
||||
request_start({ ...ctx, error: 1 });
|
||||
return;
|
||||
}
|
||||
reply = json(stdout.read('all'));
|
||||
stdout.close();
|
||||
let reply = portal.radius_call(ctx, payload);
|
||||
if (reply['access-accept']) {
|
||||
allow_client(ctx);
|
||||
portal.allow_client(ctx);
|
||||
return;
|
||||
}
|
||||
include('error.uc', ctx);
|
||||
}
|
||||
|
||||
global.handle_request = function(env) {
|
||||
let mac;
|
||||
let form_data = {};
|
||||
let query_string = {};
|
||||
let post_data = '';
|
||||
let ctx = { env, header, footer, mac, form_data, post_data, query_string, config };
|
||||
let ctx = portal.handle_request(env);
|
||||
|
||||
// lookup the peers MAC
|
||||
let macs = rtnl.request(rtnl.const.RTM_GETNEIGH, rtnl.const.NLM_F_DUMP, { });
|
||||
for (let m in macs)
|
||||
if (m.dst == env.REMOTE_HOST)
|
||||
ctx.mac = replace(m.lladdr, ':', '-');
|
||||
|
||||
// if the MAC lookup failed, go to the error page
|
||||
if (!ctx.mac)
|
||||
include('error.uc', ctx);
|
||||
|
||||
// split QUERY_STRING
|
||||
if (env.QUERY_STRING)
|
||||
for (let chunk in split(env.QUERY_STRING, '&')) {
|
||||
let m = match(chunk, /^([^=]+)=(.*)$/);
|
||||
if (!m) continue;
|
||||
ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20));
|
||||
}
|
||||
auth_client(ctx);
|
||||
if (ctx)
|
||||
auth_client(ctx);
|
||||
};
|
||||
|
||||
%}
|
||||
|
||||
@@ -2,46 +2,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let fs = require('fs');
|
||||
let rtnl = require('rtnl');
|
||||
let uam = require('uam');
|
||||
|
||||
let uci = require('uci').cursor();
|
||||
let config = uci.get_all('uspot');
|
||||
|
||||
let file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/header.html' : '/usr/share/uspot/header', 'r');
|
||||
let header = file.read('all');
|
||||
file.close();
|
||||
|
||||
file = fs.open(config.config.web_root == 1 ? '/tmp/ucentral/www-uspot/footer.html' : '/usr/share/uspot/footer', 'r');
|
||||
let footer = file.read('all');
|
||||
file.close();
|
||||
|
||||
// fs.open wrapper
|
||||
function fs_open(cmd) {
|
||||
let stdout = fs.popen(cmd);
|
||||
if (!stdout)
|
||||
return null;
|
||||
|
||||
let reply = null;
|
||||
try {
|
||||
reply = json(stdout.read('all'));
|
||||
} catch(e) {
|
||||
|
||||
}
|
||||
stdout.close();
|
||||
return reply;
|
||||
}
|
||||
|
||||
// give a client access to the internet
|
||||
function allow_client(ctx) {
|
||||
system('ubus call spotfilter client_set \'{ "interface": "hotspot", "address": "' + replace(ctx.mac, '-', ':') + '", "state": 1, "dns_state": 1}\'');
|
||||
include('allow.uc', ctx);
|
||||
}
|
||||
push(REQUIRE_SEARCH_PATH, "/usr/share/uspot/*.uc");
|
||||
let portal = require('common');
|
||||
|
||||
// delegate an initial connection to the correct handler
|
||||
function request_start(ctx) {
|
||||
switch (config?.config?.auth_mode) {
|
||||
switch (portal.config?.config?.auth_mode) {
|
||||
case 'click-to-continue':
|
||||
include('click.uc', ctx);
|
||||
return;
|
||||
@@ -52,17 +18,17 @@ function request_start(ctx) {
|
||||
include('radius.uc', ctx);
|
||||
return;
|
||||
case 'uam':
|
||||
ctx.uam_location = config.uam.uam_server +
|
||||
ctx.redir_location = portal.config.uam.uam_server +
|
||||
'?res=notyet' +
|
||||
'&uamip=' + ctx.env.SERVER_ADDR +
|
||||
'&uamport=' + config.uam.uam_port +
|
||||
'&challenge=' + uam.md5(config.uam.challenge, ctx.mac) +
|
||||
'&uamport=' + portal.config.uam.uam_port +
|
||||
'&challenge=' + portal.uam.md5(portal.config.uam.challenge, ctx.mac) +
|
||||
'&mac=' + replace(ctx.mac, ':', '-') +
|
||||
'&ip=' + ctx.env.REMOTE_ADDR +
|
||||
'&called=' + config.uam.nasmac +
|
||||
'&nasid=' + config.uam.nasid;
|
||||
ctx.uam_location += '&md=' + uam.md5(ctx.uam_location, config.uam.uam_secret);
|
||||
include('uam.uc', ctx);
|
||||
'&called=' + portal.config.uam.nasmac +
|
||||
'&nasid=' + portal.config.uam.nasid;
|
||||
ctx.redir_location += '&md=' + portal.uam.md5(ctx.uam_location, portal.config.uam.uam_secret);
|
||||
include('redir.uc', ctx);
|
||||
return;
|
||||
default:
|
||||
include('error.uc', ctx);
|
||||
@@ -73,7 +39,7 @@ function request_start(ctx) {
|
||||
// delegate a local click-to-continue authentication
|
||||
function request_click(ctx) {
|
||||
// make sure this is the right auth_mode
|
||||
if (config?.config?.auth_mode != 'click-to-continue') {
|
||||
if (portal.config?.config?.auth_mode != 'click-to-continue') {
|
||||
include('error.uc', ctx);
|
||||
return;
|
||||
}
|
||||
@@ -83,13 +49,13 @@ function request_click(ctx) {
|
||||
request_start({ ...ctx, error: 1 });
|
||||
return;
|
||||
}
|
||||
allow_client(ctx);
|
||||
portal.allow_client(ctx);
|
||||
}
|
||||
|
||||
// delegate a local username/password authentication
|
||||
function request_credentials(ctx) {
|
||||
// make sure this is the right auth_mode
|
||||
if (config?.config?.auth_mode != 'credentials') {
|
||||
if (portal/config?.config?.auth_mode != 'credentials') {
|
||||
include('error.uc', ctx);
|
||||
return;
|
||||
}
|
||||
@@ -101,8 +67,8 @@ function request_credentials(ctx) {
|
||||
}
|
||||
|
||||
// check if the credentials are valid
|
||||
for (let k in config) {
|
||||
let cred = config[k];
|
||||
for (let k in portal.config) {
|
||||
let cred = portal.config[k];
|
||||
|
||||
if (cred['.type'] != 'credentials')
|
||||
continue;
|
||||
@@ -110,7 +76,7 @@ function request_credentials(ctx) {
|
||||
ctx.form_data.password != cred.password)
|
||||
continue;
|
||||
|
||||
allow_client(ctx);
|
||||
portal.allow_client(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,7 +87,7 @@ function request_credentials(ctx) {
|
||||
// delegate a radius username/password authentication
|
||||
function request_radius(ctx) {
|
||||
// make sure this is the right auth_mode
|
||||
if (config?.config?.auth_mode != 'radius') {
|
||||
if (portal.config?.config?.auth_mode != 'radius') {
|
||||
include('error.uc', ctx);
|
||||
return;
|
||||
}
|
||||
@@ -132,29 +98,15 @@ function request_radius(ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cfg = fs.open('/tmp/' + ctx.mac + '.json', 'w');
|
||||
cfg.write({
|
||||
type: 'auth',
|
||||
server: sprintf('%s:%s:%s',config.radius.auth_server, config.radius.auth_port, config.radius.auth_secret),
|
||||
username: ctx.form_data.username,
|
||||
password: ctx.form_data.password,
|
||||
acct_session: "0123456789",
|
||||
client_ip: ctx.env.REMOTE_ADDR,
|
||||
called_station: ctx.mac,
|
||||
nas_ip: ctx.env.SERVER_ADDR,
|
||||
});
|
||||
cfg.close();
|
||||
// trigger the radius auth
|
||||
let payload = radius_init(ctx);
|
||||
payload.type = 'auth';
|
||||
payload.username = ctx.form_data.username;
|
||||
payload.password = ctx.form_data.password;
|
||||
|
||||
let stdout = fs.popen('/usr/bin/radius-client /tmp/' + ctx.mac + '.json');
|
||||
let reply;
|
||||
if (!stdout) {
|
||||
request_start({ ...ctx, error: 1 });
|
||||
return;
|
||||
}
|
||||
reply = json(stdout.read('all'));
|
||||
stdout.close();
|
||||
let reply = portal.radius_call(ctx, payload);
|
||||
if (reply['access-accept']) {
|
||||
allow_client(ctx);
|
||||
portal.allow_client(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -162,70 +114,24 @@ function request_radius(ctx) {
|
||||
request_start({ ...ctx, error: 1 });
|
||||
}
|
||||
|
||||
function PO(id, english) {
|
||||
return english;
|
||||
}
|
||||
|
||||
global.handle_request = function(env) {
|
||||
let mac;
|
||||
let form_data = {};
|
||||
let query_string = {};
|
||||
let post_data = '';
|
||||
let ctx = { env, header, footer, mac, form_data, post_data, query_string, config, PO };
|
||||
let ctx = portal.handle_request(env);
|
||||
|
||||
// lookup the peers MAC
|
||||
let macs = rtnl.request(rtnl.const.RTM_GETNEIGH, rtnl.const.NLM_F_DUMP, { });
|
||||
for (let m in macs)
|
||||
if (m.dst == env.REMOTE_HOST)
|
||||
ctx.mac = replace(m.lladdr, ':', '-');
|
||||
|
||||
// if the MAC lookup failed, go to the error page
|
||||
if (!ctx.mac)
|
||||
include('error.uc', ctx);
|
||||
|
||||
// check if a client is already connected
|
||||
let connected = fs_open('ubus call spotfilter client_get \'{"interface": "hotspot", "address": "' + ctx.mac + '"}\'');
|
||||
if (connected?.state) {
|
||||
include('connected.uc', ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// split QUERY_STRING
|
||||
if (env.QUERY_STRING)
|
||||
for (let chunk in split(env.QUERY_STRING, '&')) {
|
||||
let m = match(chunk, /^([^=]+)=(.*)$/);
|
||||
if (!m) continue;
|
||||
ctx.query_string[m[1]] = replace(m[2], /%([[:xdigit:]][[:xdigit:]])/g, (m, h) => chr(hex(h) || 0x20));
|
||||
if (ctx)
|
||||
switch (ctx.form_data.action) {
|
||||
case 'credentials':
|
||||
request_credentials(ctx);
|
||||
return;
|
||||
case 'radius':
|
||||
request_radius(ctx);
|
||||
return;
|
||||
case 'click':
|
||||
request_click(ctx);
|
||||
return;
|
||||
default:
|
||||
request_start(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// recv POST data
|
||||
if (env.CONTENT_LENGTH > 0)
|
||||
for (let chunk = uhttpd.recv(64); chunk != null; chunk = uhttpd.recv(64))
|
||||
post_data += replace(chunk, /[^[:graph:]]/g, '.');
|
||||
|
||||
// split POST data into an array
|
||||
if (post_data)
|
||||
for (let chunk in split(post_data, '&')) {
|
||||
let var = split(chunk, '=');
|
||||
if (length(var) != 2)
|
||||
continue;
|
||||
ctx.form_data[var[0]] = var[1];
|
||||
}
|
||||
|
||||
switch (ctx.form_data.action) {
|
||||
case 'credentials':
|
||||
request_credentials(ctx);
|
||||
return;
|
||||
case 'radius':
|
||||
request_radius(ctx);
|
||||
return;
|
||||
case 'click':
|
||||
request_click(ctx);
|
||||
return;
|
||||
default:
|
||||
request_start(ctx);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
%}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Status: 302 Found
|
||||
Location: {{ uam_location }}
|
||||
Location: {{ redir_location }}
|
||||
Content-Type: text/html
|
||||
|
||||
|
||||
Reference in New Issue
Block a user