218 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| var oauth3 = require('./oauth3.js');
 | |
| var defaults = {
 | |
|   main: 'oauth3'
 | |
| , provider: 'oauth3.org'
 | |
| };
 | |
| 
 | |
| function parseArgs(argv, opts) {
 | |
|   var args = Array.prototype.slice.call(argv);
 | |
|   var sep = /[:\.\-]/;
 | |
| 
 | |
|   args.shift(); // 'node' is the first parameter
 | |
|   args.shift(); // 'oauth3.js' will be the
 | |
| 
 | |
|   var command = args.shift() || 'help';
 | |
|   var cmdpair = command.split(sep);
 | |
|   var cmd = cmdpair[0];
 | |
|   var sub = cmdpair[1];
 | |
|   var COMMAND = 'COMMAND';
 | |
|   var maxCmdLen = COMMAND.length;
 | |
|   var maxPairLen = 0;
 | |
|   var arg1 = args[0];
 | |
| 
 | |
|   // build top-level commands (tlcs) list
 | |
|   // also count the word-width (for the space needed to print the commands)
 | |
|   var pairsMap = {};
 | |
|   var tlcs = opts.commands.filter(function (desc) {
 | |
|     var pair = desc[0].split(/\s+/)[0];
 | |
|     var psub = pair.split(sep)[0];
 | |
|     pairsMap[pair] = true;
 | |
|     maxPairLen = Math.max(maxPairLen, pair.length);
 | |
|     if (pair === psub) {
 | |
|       maxCmdLen = Math.max(maxCmdLen, psub.length);
 | |
|       return true;
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   // right pad (for making the printed lines longer)
 | |
|   function rpad(str, len) {
 | |
|     while (str.length < len) {
 | |
|       str += ' ';
 | |
|     }
 | |
|     return str;
 | |
|   }
 | |
| 
 | |
|   // oauth3.js help
 | |
|   // oauth3.js help <command>
 | |
|   // oauth3.js help <command:sub> (alias of `oauth3.js <command:sub> --help')
 | |
|   function help() {
 | |
|     var status = 0;
 | |
| 
 | |
|     function printCmd(desc) {
 | |
|       var pcmd = rpad(desc[0].split(/\s+/)[0], maxCmdLen);
 | |
|       var pdesc = desc[1];
 | |
|       console.info('\t' + defaults.main + ' ' + pcmd, ' # ' + pdesc);
 | |
|     }
 | |
| 
 | |
|     function printCmds(cmds) {
 | |
|       console.info('');
 | |
| 
 | |
|       var title = defaults.main + ' ' + rpad(COMMAND, maxCmdLen) + '  # description';
 | |
|       var bars = title.replace(/./g, '-').split('');
 | |
|       bars[bars.length - ' # description'.length] = ' ';
 | |
|       bars[bars.length - (' # description'.length + 1)] = ' ';
 | |
|       console.info('\t' + title);
 | |
|       console.info('\t' + bars.join(''));
 | |
|       cmds.forEach(printCmd);
 | |
|       console.info('');
 | |
|     }
 | |
| 
 | |
|     function helpMain() {
 | |
|       console.info('');
 | |
|       console.info('Here are all the top-level commands:');
 | |
| 
 | |
|       printCmds(tlcs);
 | |
|     }
 | |
| 
 | |
|     if (arg1 && -1 === Object.keys(pairsMap).indexOf(arg1)) {
 | |
|       status = 1;
 | |
|       console.info('');
 | |
|       console.info(defaults.main + ": Unknown command '" + arg1 + "'");
 | |
|       console.info('');
 | |
|       console.info("Try '" + defaults.main + " help'");
 | |
|       console.info('');
 | |
|       arg1 = null;
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // the case of "oauth3 help --something"
 | |
|     if (!arg1 || '-' === arg1[0]) {
 | |
|       helpMain();
 | |
|       process.exit(status);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // the case of "oauth3 help help"
 | |
|     if ('help' === arg1) {
 | |
|       helpMain();
 | |
|       console.info("no more help available for 'help'");
 | |
|       process.exit(status);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // matches the first part of the command
 | |
|     // and has second parts
 | |
|     if (arg1 === arg1.split(':')[0] && opts.commands.filter(function (desc) {
 | |
|       return arg1 === desc[0].split(/\s+/)[0].split(':')[0] && desc[0].split(/\s+/)[0].split(':');
 | |
|     }).length > 1) {
 | |
|       console.info('');
 | |
|       console.info("Here are all the '" + command + "'-related commands:");
 | |
|       printCmds(
 | |
|         opts.commands.filter(function (desc) {
 | |
|           var pair = desc[0].split(/\s+/)[0];
 | |
|           var psub = pair.split(sep)[0];
 | |
|           maxPairLen = Math.max(maxPairLen, pair.length);
 | |
|           if (arg1 === psub || arg1 === pair) {
 | |
|             maxCmdLen = Math.max(maxCmdLen, pair.length);
 | |
|             return true;
 | |
|           }
 | |
|         })
 | |
|       );
 | |
|       console.info('');
 | |
|     } else {
 | |
|       console.info('');
 | |
|       console.info("Here are all the options and flags for '" + arg1 + "':");
 | |
|       console.info('');
 | |
|       opts.commands.some(function (desc) {
 | |
|         var pair = desc[0].split(/\s+/)[0];
 | |
|         var psub = pair.split(sep)[0];
 | |
|         maxPairLen = Math.max(maxPairLen, pair.length);
 | |
|         if (arg1 !== psub && arg1 !== pair) {
 | |
|           return false;
 | |
|         }
 | |
|         maxCmdLen = Math.max(maxCmdLen, pair.length);
 | |
|         console.log('\t' + desc[0] + '\t# ' + desc[1]);
 | |
|         (desc[2]||[]).forEach(function (flag) {
 | |
|           var pair = flag.split(', ');
 | |
|           var f = pair.shift();
 | |
|           var d = pair.join(', ');
 | |
|           console.log('\t\t' + f + ' # ' + d);
 | |
|         });
 | |
|         return true;
 | |
|       });
 | |
|       console.info('');
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If the command is not in the list of commands
 | |
|   if (-1 === Object.keys(pairsMap).indexOf(cmd)) {
 | |
|     arg1 = cmd;
 | |
|     cmd = 'help';
 | |
|     help();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If help is explictly requested
 | |
|   if (-1 !== [ 'help', '-h', '--help' ].indexOf(command) || -1 !== args.indexOf('-h') || -1 !== args.indexOf('--help')) {
 | |
|     help();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If we're ready to rock and roll!
 | |
|   console.log('RUN', cmd, sub || '(n/a)', arg1 || '(n/a)', '... not yet implemented');
 | |
| }
 | |
| 
 | |
| parseArgs(process.argv, {
 | |
|   // CLI goals:
 | |
|   //
 | |
|   // whoami / login: you are now logged in as
 | |
|   //   * john@example.com [current] (just now)
 | |
|   //   * john@work.net (2 minutes ago)
 | |
|   //   * john@family.me (2 weeks ago)
 | |
|   commands: [
 | |
|     [ 'login [email or cloud address]', 'alias of session:attach', [
 | |
|         "--auto, create a new account without asking if none exists"
 | |
|       //, "--exclusive, logout all other ids, removing access to their accounts"
 | |
|       , "--provider, specify an authentication provider (default: :provider)".replace(/\b:provider\b/, defaults.provider)
 | |
|       //, "--email [addr], use the given id as an email address, even if it works as a cloud address"
 | |
|       //, "--cloud [addr], use the given id as a cloud address or fail (don't fallback to email)"
 | |
|       ]
 | |
|     ]
 | |
|   , [ 'logout', 'alias of session:detach' ]
 | |
|   , [ 'whoami', 'show current account(s) and login(s) and device(s)' ]
 | |
| 
 | |
|     // authn
 | |
|   , [ 'session', 'Manage your ids (credentials / logins)' ]
 | |
|   , [ 'session:new', 'alias of `login --exclusive`' ]
 | |
|   , [ 'session:attach', 'Create a session (and account if needed) for a given email address or cloud address' ]
 | |
|   , [ 'session:detach', 'remove login from session' ]
 | |
|   , [ 'session:list', 'show all of the ids in the current session' ]
 | |
| 
 | |
|     // authz
 | |
|   , [ 'accounts', 'Manage your accounts (authorization / profiles)' ]
 | |
|   , [ 'accounts:new', 'create a new account attached to the credentials of the current session' ]
 | |
|   , [ 'accounts:set', 'change account details' ] // todo changing the name should be restricted john@provider.net -> jonathan@provider.net would be bad
 | |
|   , [ 'accounts:list', 'show all of the accounts in the current session' ]
 | |
|   , [ 'accounts:attach', 'attach an account to an id' ]
 | |
|   , [ 'accounts:detach', 'detach an account from an id' ]
 | |
|   , [ 'accounts:select', 'select an account to use as the primary account for this session' ]
 | |
|   , [ 'accounts:update', '(deprecated) alias of set' ]
 | |
|   , [ 'accounts:login', '(deprecated) alias of login' ]
 | |
|   , [ 'accounts:whoami', '(deprecated) alias of whoami' ]
 | |
| 
 | |
|     // authn / authz
 | |
|   , [ 'devices', 'manages devices for your account(s)' ]
 | |
|   , [ 'devices:new', 'create a new device (default name is hostname, default ip is the result of :provider/api/tunnel@oauth3.org/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
 | |
|   , [ 'devices:set', 'set the ip address of the device (defaults ip is the result of :provider/api/tunnel@oauth3.org/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
 | |
|   , [ 'devices:attach', "attach a device to a domain's DNS record" ]
 | |
|   , [ 'devices:detach', "detach an account from a domain's DNS record" ]
 | |
|   , [ 'devices:select', '(re)claim the specified device as this device (i.e. you re-installed your OS or deleted your ~/.oauth3)' ]
 | |
|   , [ 'devices:list', 'show all devices for your account(s)' ]
 | |
| 
 | |
|     // help
 | |
|   , [ 'help', "show this menu; use '" + defaults.main + " help COMMAND' (even 'help') for options and sub-commands" ]
 | |
|   ]
 | |
| });
 |