comparison mod_client_management/mod_client_management.lua @ 5653:62c6e17a5e9d

Merge
author Stephen Paul Weber <singpolyma@singpolyma.net>
date Mon, 18 Sep 2023 08:24:19 -0500
parents f16edebb1305
children c69320fc438b
comparison
equal deleted inserted replaced
5652:eade7ff9f52c 5653:62c6e17a5e9d
8 local jid = require "util.jid"; 8 local jid = require "util.jid";
9 local st = require "util.stanza"; 9 local st = require "util.stanza";
10 10
11 local strict = module:get_option_boolean("enforce_client_ids", false); 11 local strict = module:get_option_boolean("enforce_client_ids", false);
12 12
13 module:default_permission("prosody:user", ":list-clients"); 13 module:default_permission("prosody:registered", ":list-clients");
14 module:default_permission("prosody:user", ":manage-clients"); 14 module:default_permission("prosody:registered", ":manage-clients");
15 15
16 local tokenauth = module:depends("tokenauth"); 16 local tokenauth = module:depends("tokenauth");
17 local mod_fast = module:depends("sasl2_fast"); 17 local mod_fast = module:depends("sasl2_fast");
18 18
19 local client_store = assert(module:open_store("clients", "keyval+")); 19 local client_store = assert(module:open_store("clients", "keyval+"));
33 local sasl_agent = sasl_handler and sasl_handler.user_agent; 33 local sasl_agent = sasl_handler and sasl_handler.user_agent;
34 local token_agent = token_info and token_info.data and token_info.data.oauth2_client; 34 local token_agent = token_info and token_info.data and token_info.data.oauth2_client;
35 if not (sasl_agent or token_agent) then return; end 35 if not (sasl_agent or token_agent) then return; end
36 return { 36 return {
37 software = sasl_agent and sasl_agent.software or token_agent and token_agent.name or nil; 37 software = sasl_agent and sasl_agent.software or token_agent and token_agent.name or nil;
38 software_id = token_agent and token_agent.id or nil;
39 software_version = token_agent and token_agent.version or nil;
38 uri = token_agent and token_agent.uri or nil; 40 uri = token_agent and token_agent.uri or nil;
39 device = sasl_agent and sasl_agent.device or nil; 41 device = sasl_agent and sasl_agent.device or nil;
40 }; 42 };
41 end 43 end
42 44
248 table.insert(active_clients, { 250 table.insert(active_clients, {
249 id = "grant/"..grant_id; 251 id = "grant/"..grant_id;
250 type = "access"; 252 type = "access";
251 first_seen = grant.created; 253 first_seen = grant.created;
252 last_seen = grant.accessed; 254 last_seen = grant.accessed;
255 expires = grant.expires;
253 active = { 256 active = {
254 grant = grant; 257 grant = grant;
255 }; 258 };
256 user_agent = get_user_agent(nil, grant); 259 user_agent = get_user_agent(nil, grant);
257 }); 260 });
272 end 275 end
273 return a.id < b.id; 276 return a.id < b.id;
274 end); 277 end);
275 278
276 return active_clients; 279 return active_clients;
280 end
281
282 local function user_agent_tostring(user_agent)
283 if user_agent then
284 if user_agent.software then
285 if user_agent.software_version then
286 return user_agent.software .. "/" .. user_agent.software_version;
287 end
288 return user_agent.software;
289 end
290 end
277 end 291 end
278 292
279 function revoke_client_access(username, client_selector) 293 function revoke_client_access(username, client_selector)
280 if client_selector then 294 if client_selector then
281 local c_type, c_id = client_selector:match("^(%w+)/(.+)$"); 295 local c_type, c_id = client_selector:match("^(%w+)/(.+)$");
307 return nil, "item-not-found"; 321 return nil, "item-not-found";
308 end 322 end
309 local ok = tokenauth.revoke_grant(username, c_id); 323 local ok = tokenauth.revoke_grant(username, c_id);
310 if not ok then return nil, "internal-server-error"; end 324 if not ok then return nil, "internal-server-error"; end
311 return true; 325 return true;
326 elseif c_type == "software" then
327 local active_clients = get_active_clients(username);
328 for _, client in ipairs(active_clients) do
329 if client.user_agent and client.user_agent.software == c_id or user_agent_tostring(client.user_agent) == c_id then
330 return revoke_client_access(username, client.id);
331 end
332 end
312 end 333 end
313 end 334 end
314 335
315 return nil, "item-not-found"; 336 return nil, "item-not-found";
316 end 337 end
346 end 367 end
347 368
348 local user_agent = st.stanza("user-agent"); 369 local user_agent = st.stanza("user-agent");
349 if client.user_agent then 370 if client.user_agent then
350 if client.user_agent.software then 371 if client.user_agent.software then
351 user_agent:text_tag("software", client.user_agent.software); 372 user_agent:text_tag("software", client.user_agent.software, { id = client.user_agent.software_id; version = client.user_agent.software_version });
352 end 373 end
353 if client.user_agent.device then 374 if client.user_agent.device then
354 user_agent:text_tag("device", client.user_agent.device); 375 user_agent:text_tag("device", client.user_agent.device);
355 end 376 end
356 if client.user_agent.uri then 377 if client.user_agent.uri then
415 local clients = mod.get_active_clients(username); 436 local clients = mod.get_active_clients(username);
416 if not clients or #clients == 0 then 437 if not clients or #clients == 0 then
417 return true, "No clients associated with this account"; 438 return true, "No clients associated with this account";
418 end 439 end
419 440
441 local function date_or_time(last_seen)
442 return last_seen and os.date(math.abs(os.difftime(os.time(), last_seen)) >= 86400 and "%Y-%m-%d" or "%H:%M:%S", last_seen);
443 end
444
445 local date_or_time_width = math.max(#os.date("%Y-%m-%d"), #os.date("%H:%M:%S"));
446
420 local colspec = { 447 local colspec = {
448 { title = "ID"; key = "id"; width = "1p" };
421 { 449 {
422 title = "Software"; 450 title = "Software";
423 key = "user_agent"; 451 key = "user_agent";
424 width = "1p"; 452 width = "1p";
425 mapper = function(user_agent) 453 mapper = user_agent_tostring;
426 return user_agent and user_agent.software; 454 };
427 end; 455 {
456 title = "First seen";
457 key = "first_seen";
458 width = date_or_time_width;
459 align = "right";
460 mapper = date_or_time;
428 }; 461 };
429 { 462 {
430 title = "Last seen"; 463 title = "Last seen";
431 key = "last_seen"; 464 key = "last_seen";
432 width = math.max(#os.date("%Y-%m-%d"), #os.date("%H:%M:%S")); 465 width = date_or_time_width;
433 align = "right"; 466 align = "right";
434 mapper = function(last_seen) 467 mapper = date_or_time;
435 return os.date(os.difftime(os.time(), last_seen) >= 86400 and "%Y-%m-%d" or "%H:%M:%S", last_seen); 468 };
436 end; 469 {
470 title = "Expires";
471 key = "expires";
472 width = date_or_time_width;
473 align = "right";
474 mapper = date_or_time;
437 }; 475 };
438 { 476 {
439 title = "Authentication"; 477 title = "Authentication";
440 key = "active"; 478 key = "active";
441 width = "2p"; 479 width = "2p";
454 print(row(client)); 492 print(row(client));
455 end 493 end
456 print(string.rep("-", self.session.width)); 494 print(string.rep("-", self.session.width));
457 return true, ("%d clients"):format(#clients); 495 return true, ("%d clients"):format(#clients);
458 end 496 end
497
498 function console_env.user:revoke_client(user_jid, selector) -- luacheck: ignore 212/self
499 local username, host = jid.split(user_jid);
500 local mod = prosody.hosts[host] and prosody.hosts[host].modules.client_management;
501 if not mod then
502 return false, ("Host does not exist on this server, or does not have mod_client_management loaded");
503 end
504
505 local revoked, err = revocation_errors.coerce(mod.revoke_client_access(username, selector));
506 if not revoked then
507 return false, err.text or err;
508 end
509 return true, "Client access revoked";
510 end
459 end); 511 end);