Improvements to oputil

* Major update to --help
* '2fa' is now '2fa-otp' or just 'otp'
* Better 2fa-otp output & handling
This commit is contained in:
Bryan Ashby 2019-05-11 00:21:42 -06:00
parent 6953cdf159
commit d215919bff
No known key found for this signature in database
GPG key ID: B49EB437951D2542
3 changed files with 180 additions and 97 deletions

View file

@ -209,7 +209,7 @@ function scanFileAreaForChanges(areaInfo, options, cb) {
async.series( async.series(
[ [
function quickCheck(next) { function quickCheck(next) {
if(!options.quick) { if(options['full-scan']) {
return next(null); return next(null);
} }
@ -476,8 +476,8 @@ function scanFileAreas() {
options.tags = tags.split(','); options.tags = tags.split(',');
} }
options.descFile = argv['desc-file']; // --desc-file or --desc-file PATH options.descFile = argv['desc-file']; // --desc-file or --desc-file PATH
options.quick = argv.quick; options['full-scan'] = argv['-full-scan'];
options.areaAndStorageInfo = getAreaAndStorage(argv._.slice(2)); options.areaAndStorageInfo = getAreaAndStorage(argv._.slice(2));

View file

@ -9,115 +9,169 @@ exports.getHelpFor = getHelpFor;
const usageHelp = exports.USAGE_HELP = { const usageHelp = exports.USAGE_HELP = {
General : General :
`usage: oputil.js [--version] [--help] `usage: oputil.js [--version] [--help]
<command> [<args>] <command> [<arguments>]
global args: global arguments:
-c, --config PATH specify config path (${getDefaultConfigPath()}) -c, --config PATH Specify config path (${getDefaultConfigPath()})
-n, --no-prompt assume defaults/don't prompt for input where possible -n, --no-prompt Assume defaults (don't prompt for input where possible)
commands: commands:
user user utilities user User management
config config file management config Configuration management
fb file base management fb File base management
mb message base management mb Message base management
`, `,
User : User :
`usage: oputil.js user <action> [<args>] `usage: oputil.js user <action> [<arguments>]
actions: Actions:
info USERNAME display information about a user info USERNAME Display information about a user
pw USERNAME PASSWORD set a user's password
aliases: password, passwd pw USERNAME PASSWORD Set a user's password
rm USERNAME permanently removes user from system (passwd|password)
aliases: remove, delete, del
rename USERNAME NEWNAME rename a user rm USERNAME Permanently removes user from system
aliases: mv (del|delete|remove)
activate USERNAME set status to active
deactivate USERNAME set status to inactive rename USERNAME NEWNAME Rename a user
disable USERNAME set status to disabled (mv)
lock USERNAME set status to locked
group USERNAME [+|-]GROUP adds (+) or removes (-) user from a group 2fa-otp USERNAME SPEC Enable Two Factor Authentication (2FA)
(otp)
Valid specs:
totp : Time-Based One-Time Password Algorithm (RFC-6238)
hotp : HMAC-Based One-Time Password Algorithm (RFC-4266)
google : Google Authenticator
activate USERNAME Set a user's status to "active"
deactivate USERNAME Set a user's status to "inactive"
disable USERNAME Set a user's status to "disabled"
lock USERNAME Set a user's status to "locked"
group USERNAME [+|-]GROUP Adds (+) or removes (-) user from a group
info arguments:
--security Include security information in output
2fa-otp arguments:
--qr-type TYPE Specify QR code type
Valid QR types:
ascii : Plain ASCII (default)
data : HTML data URL
img : HTML image tag
svg : SVG image
--out PATH Path to write QR code to. defaults to stdout
`, `,
Config : Config :
`usage: oputil.js config <action> [<args>] `usage: oputil.js config <action> [<arguments>]
actions: Actions:
new generate a new/initial configuration new Generate a new / default configuration
cat cat current configuration to stdout
cat args: cat Write current configuration to stdout
--no-color disable color
--no-comments strip any comments cat arguments:
--no-color Disable color
--no-comments Strip any comments
`, `,
FileBase : FileBase :
`usage: oputil.js fb <action> [<args>] `usage: oputil.js fb <action> [<arguments>]
actions: Actions:
scan AREA_TAG[@STORAGE_TAG] scan specified area scan AREA_TAG[@STORAGE_TAG] Scan specified area
may also contain optional GLOB as last parameter,
for example: scan some_area *.zip
info CRITERIA display information about areas and/or files May contain optional GLOB as last parameter.
matching CRITERIA. Example: ./oputil.js fb scan d0pew4r3z *.zip
mv SRC [SRC...] DST move entry(s) from SRC to DST info CRITERIA Display information about areas and/or files
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG]
DST: AREA_TAG[@STORAGE_TAG]
rm SRC [SRC...] remove entry(s) from the system matching SRC mv SRC [SRC...] DST Move matching entry(s)
SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG] (move)
desc CRITERIA sets a new file description for file base entry
matching CRITERIA. Launches an external editor using
$VISUAL, $EDITOR, or vim/notepad.
import-areas FILEGATE.ZXX import file base areas using FileGate RAID type format
scan args: Source may be any of the following:
--tags TAG1,TAG2,... specify tag(s) to assign to discovered entries - Filename including '*' wildcards
- SHA-1
- File ID
- Area tag with optional @storageTag suffix
Destination is area tag with optional @storageTag suffix
--desc-file [PATH] prefer file descriptions from supplied path over other rm SRC [SRC...] Remove entry(s) from the system
other sources such as FILE_ID.DIZ. Path must point to (del|delete|remove)
a valid FILES.BBS or DESCRIPT.ION file.
--update attempt to update information for existing entries
--quick perform quick scan
info args: Source may be any of the following:
--show-desc display short description, if any - Filename including '*' wildcards
- SHA-1
- File ID
- Area tag with optional @storageTag suffix
remove args: desc CRITERIA Updates an file base entry's description
--phys-file also remove underlying physical file
import-areas args: Launches an external editor using $VISUAL, $EDITOR, or vim/notepad.
--type TYPE sets import areas type. valid options are "zxx" or "na"
--create-dirs create backing storage directories import-areas FILEGATE.ZXX Import file base areas using FileGate RAID type format
scan arguments:
--tags TAG1,TAG2,... Specify hashtag(s) to assign to discovered entries
--desc-file [PATH] Prefer file descriptions from supplied input file
If a file description can be found in the supplied input file, prefer that description
over other sources such related FILE_ID.DIZ. Path must point to a valid FILES.BBS or
DESCRIPT.ION file.
--update Attempt to update information for existing entries
--full-scan Perform a full scan (default is quick)
info arguments:
--show-desc Display short description, if any
remove arguments:
--phys-file Also remove underlying physical file
import-areas arguments:
--type TYPE Sets import areas type
Valid types are are "zxx" or "na".
--create-dirs Also create backing storage directories
`, `,
FileOpsInfo : FileOpsInfo :
` `
general information: General Information:
AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag Generally an area tag can also include an optional storage tag. For example, the
example: retro@bbs area of 'bbswarez' stored using 'bbswarez_main': bbswarez@bbswarez_main
CRITERIA file base entry criteria. in general, can be AREA_TAG, SHA, When performing an initial import of a large area or storage backing, --full-scan
FILE_ID, or FILENAME_WC. is the best option. If re-scanning an area for updates a standard / quick scan is
generally good enough.
FILENAME_WC filename with * and ? wildcard support. may match 0:n entries
SHA full or partial SHA-256 File ID's are those found in file.sqlite3.
FILE_ID a file identifier. see file.sqlite3
`, `,
MessageBase : MessageBase :
`usage: oputil.js mb <action> [<args>] `usage: oputil.js mb <action> [<arguments>]
actions: actions:
areafix CMD1 CMD2 ... ADDR sends an AreaFix NetMail to ADDR with the supplied command(s) areafix CMD1 CMD2 ... ADDR Sends an AreaFix NetMail
one or more commands may be supplied. commands that are multi
part such as "%COMPRESS ZIP" should be quoted.
import-areas PATH import areas using fidonet *.NA or AREAS.BBS file from PATH
import-areas args: NetMail is sent to supplied address with the supplied command(s). Multi-part commands
--conf CONF_TAG conference tag in which to import areas such as "%COMPRESS ZIP" should be quoted.
--network NETWORK network name/key to associate FTN areas
--uplinks UL1,UL2,... one or more comma separated uplinks import-areas PATH Import areas using FidoNet *.NA or AREAS.BBS file
--type TYPE area import type. valid options are "bbs" and "na"
import-areas arguments:
--conf CONF_TAG Conference tag in which to import areas
--network NETWORK Network name/key to associate FTN areas
--uplinks UL1,UL2,... One or more uplinks (comma separated)
--type TYPE Area import type
Valid types are "bbs" and "na".
` `
}; };

View file

@ -329,7 +329,7 @@ function showUserInfo(user) {
return user.properties[p] || 'N/A'; return user.properties[p] || 'N/A';
}; };
console.info(`User information: const stdInfo = `User information:
Username : ${user.username}${user.isRoot() ? ' (root/SysOp)' : ''} Username : ${user.username}${user.isRoot() ? ' (root/SysOp)' : ''}
Real name : ${propOrNA(UserProps.RealName)} Real name : ${propOrNA(UserProps.RealName)}
ID : ${user.userId} ID : ${user.userId}
@ -340,11 +340,29 @@ Last login : ${lastLogin()}
Login count : ${propOrNA(UserProps.LoginCount)} Login count : ${propOrNA(UserProps.LoginCount)}
Email : ${propOrNA(UserProps.EmailAddress)} Email : ${propOrNA(UserProps.EmailAddress)}
Location : ${propOrNA(UserProps.Location)} Location : ${propOrNA(UserProps.Location)}
Affiliations : ${propOrNA(UserProps.Affiliations)} Affiliations : ${propOrNA(UserProps.Affiliations)}`;
`); let secInfo = '';
if(argv.security) {
const otp = user.getProperty(UserProps.AuthFactor2OTP);
if(otp) {
const backupCodesOrNa = () => {
try
{
return JSON.parse(user.getProperty(UserProps.AuthFactor2OTPBackupCodes)).join(', ');
} catch(e) {
return 'N/A';
}
};
secInfo = `\n2FA OTP : ${otp}
OTP secret : ${user.getProperty(UserProps.AuthFactor2OTPSecret) || 'N/A'}
OTP Backup : ${backupCodesOrNa()}`;
}
}
console.info(`${stdInfo}${secInfo}`);
} }
function twoFactorAuth(user) { function twoFactorAuthOTP(user) {
if(argv._.length < 4) { if(argv._.length < 4) {
return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR); return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR);
} }
@ -359,8 +377,15 @@ function twoFactorAuth(user) {
function validate(callback) { function validate(callback) {
// :TODO: Prompt for if not supplied // :TODO: Prompt for if not supplied
let otpType = argv._[argv._.length - 1]; let otpType = argv._[argv._.length - 1];
// allow aliases for OTP types
otpType = {
google : OTPTypes.GoogleAuthenticator,
hotp : OTPTypes.RFC4266_HOTP,
totp : OTPTypes.RFC6238_TOTP,
}[otpType] || otpType;
otpType = _.find(OTPTypes, t => { otpType = _.find(OTPTypes, t => {
return t.toLowerCase() === otpType; return t.toLowerCase() === otpType.toLowerCase();
}); });
if(!otpType) { if(!otpType) {
return callback(Errors.Invalid('Invalid OTP type')); return callback(Errors.Invalid('Invalid OTP type'));
@ -377,7 +402,7 @@ function twoFactorAuth(user) {
}); });
}, },
function storeOrDisplayQR(otpInfo, callback) { function storeOrDisplayQR(otpInfo, callback) {
if(!argv.out) { if(!argv.out || !otpInfo.qr) {
return callback(null, otpInfo); return callback(null, otpInfo);
} }
@ -400,15 +425,18 @@ function twoFactorAuth(user) {
if(err) { if(err) {
console.error(err.message); console.error(err.message);
} else { } else {
console.info(`OTP enabled for ${user.username}.`); console.info(`OTP enabled for : ${user.username}`);
console.info(`Secret: ${otpInfo.secret}`); console.info(`Secret : ${otpInfo.secret}`);
console.info(`Backup codes: ${otpInfo.backupCodes.join(', ')}`); console.info(`Backup codes : ${otpInfo.backupCodes.join(', ')}`);
if(!argv.out) { if(otpInfo.qr) {
console.info('QR code:'); if(!argv.out) {
console.info(otpInfo.qr); console.info('--- Begin QR ---');
} else { console.info(otpInfo.qr);
console.info(`QR code saved to ${argv.out}`); console.info('--- End QR ---');
} else {
console.info(`QR code saved to ${argv.out}`);
}
} }
} }
} }
@ -429,7 +457,7 @@ function handleUserCommand() {
'pw', 'pass', 'passwd', 'password', 'pw', 'pass', 'passwd', 'password',
'group', 'group',
'mv', 'rename', 'mv', 'rename',
'2fa', '2fa-otp', 'otp'
].includes(action) ? argv._.length - 2 : argv._.length - 1; ].includes(action) ? argv._.length - 2 : argv._.length - 1;
const userName = argv._[usernameIdx]; const userName = argv._[usernameIdx];
@ -465,7 +493,8 @@ function handleUserCommand() {
info : showUserInfo, info : showUserInfo,
'2fa' : twoFactorAuth, '2fa-otp' : twoFactorAuthOTP,
otp : twoFactorAuthOTP,
}[action] || errUsage)(user, action); }[action] || errUsage)(user, action);
}); });
} }