Mercurial > prosody-modules
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); |
