Differences From
Artifact [63607cdb2b]:
421 421 [ lib.emit(false, 1, 'usage: ', `argv[0], ' actor ', umode.type.helptxt.flags, ' <xid> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
422 422 { 'actor <xid> rank <value>', 'set an actor\'s rank to <value> (remote actors cannot exercise rank-related powers, but benefit from rank immunities)' };
423 423 { 'actor <xid> degrade', 'alias for `actor <xid> rank 0`' };
424 424 { 'actor <xid> bestow <epithet>', 'bestow an epithet upon an actor' };
425 425 { 'actor <xid> instantiate', 'instantiate a remote actor, retrieving their profile and posts even if no one follows them' };
426 426 { 'actor <xid> proscribe', 'globally ban an actor from interacting with your server' };
427 427 { 'actor <xid> rehabilitate', 'lift a proscription on an actor' };
428 + { 'actor <xid> xkey [pem|der]', 'extract an actor\'s public key in either PEM or DER form' };
428 429 { 'actor <xid> purge-all <confirm-str>', 'remove all traces of a user from the database (except local user credentials -- use \27[1mauth all purge\27[m to prevent a user from accessing the instance)' };
429 430 }) ]
430 431 return 1
431 432 end
432 433 if umode.arglist.ct >= 2 then
433 434 var degrade = lib.str.cmp(umode.arglist(1),'degrade') == 0
434 435 var xid = umode.arglist(0)
................................................................................
467 468 lib.warn('completely purging actor ', usr.ptr.xid, ' and all related content from database')
468 469 dlg:actor_purge_uid(usr.ptr.id)
469 470 lib.report('actor purged')
470 471 else goto cmderr end
471 472 else goto cmderr end
472 473 else goto cmderr end
473 474 elseif lib.str.cmp(mode.arglist(0),'user') == 0 then
475 + if mode.arglist.ct < 3 then goto cmderr end
474 476 var umode: pbasic umode:parse(mode.arglist.ct, &mode.arglist(0))
475 477 if umode.help then
476 478 [ lib.emit(false, 1, 'usage: ', `argv[0], ' user ', umode.type.helptxt.flags, ' <handle> <cmd> [<args>…]', umode.type.helptxt.opts, cmdhelp {
477 479 { 'user <handle> create', 'add a new user' };
478 480 { 'user <handle> auth <type> new', '(where applicable, managed auth only) create a new authentication token of the given type for a user' };
479 481 { 'user <handle> auth <type> reset', '(where applicable, managed auth only) delete all of a user\'s authentication tokens of the given type and issue a new one' };
480 482 { 'user <handle> auth (<type>|all) purge', 'delete all credentials that would allow this user to log in (where possible)' };
481 483 { 'user <handle> (grant|revoke) (<priv>|all)', 'grant or revoke a specific power to or from a user' };
482 484 { 'user <handle> emasculate', 'strip all administrative powers and rank from a user' };
483 485 { 'user <handle> forgive', 'restore all default powers to a user' };
484 486 { 'user <handle> suspend [<timespec>]', '(e.g. \27[1muser jokester suspend 5d 6h 7m 3s\27[m to suspend "jokester" for five days, six hours, seven minutes, and three seconds) suspend a user'};
487 + { 'user <handle> xkey [pem|der]', 'extract an user\'s *private* key in either PEM or DER form' };
485 488 }) ]
486 489 return 1
487 490 end
488 491 var handle = umode.arglist(0)
489 492 var usr = dlg:actor_fetch_xid(pstr {ptr=handle, ct=lib.str.sz(handle)})
490 493 if umode.arglist.ct == 2 and lib.str.cmp(umode.arglist(1),'create')==0 then
491 494 if usr:ref() then lib.bail('that user already exists') end
................................................................................
524 527 end
525 528 end
526 529 end
527 530 end
528 531
529 532 usr.ptr.rights.powers = newprivs
530 533 dlg:actor_save_privs(usr.ptr)
534 + elseif lib.str.cmp(umode.arglist(1),'xkey') == 0 and umode.arglist.ct == 3 then
535 + if not usr then lib.bail('unknown handle') end
536 + if lib.str.cmp(umode.arglist(2),'pem') == 0 then
537 + var pk = lib.crypt.loadpriv(usr().key)
538 + if not pk.ok then
539 + lib.bail('could not parse key! this is probably a bug')
540 + end
541 + var pem: lib.crypt.pemfile
542 + if not lib.crypt.pem(false, &pk.val, &pem[0]) then
543 + lib.bail('could not convert key to PEM! this is probably a bug')
544 + end
545 + lib.io.send(1, pem, lib.str.sz(&pem[0]))
546 + pk.val:free()
547 + elseif lib.str.cmp(umode.arglist(2),'der') == 0 then
548 + -- TODO avoid dumping binary to tty
549 + lib.warn('dumping user\'s \x1b[1mprivate\x1b[m key!')
550 + lib.io.send(1, [&int8](usr().key.ptr), usr().key.ct)
551 + else lib.bail('invalid key format') end
531 552 elseif lib.str.cmp(umode.arglist(1),'auth') == 0 and umode.arglist.ct == 4 then
532 553 var reset = lib.str.cmp(umode.arglist(3),'reset') == 0
533 554 if reset or lib.str.cmp(umode.arglist(3),'new') == 0 then
534 555 -- FIXME enable resetting pws for users who have
535 556 -- not logged in yet
536 557 if not usr then lib.bail('unknown handle') end
537 558 if lib.str.cmp(umode.arglist(2),'pw') == 0 then