parsav  Diff

Differences From Artifact [63607cdb2b]:

To Artifact [aa223ca2fb]:


   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