From d215919bffb35dc019cdfc6b3343fe0c101b271c Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sat, 11 May 2019 00:21:42 -0600 Subject: [PATCH] Improvements to oputil * Major update to --help * '2fa' is now '2fa-otp' or just 'otp' * Better 2fa-otp output & handling --- core/oputil/oputil_file_base.js | 6 +- core/oputil/oputil_help.js | 210 ++++++++++++++++++++------------ core/oputil/oputil_user.js | 61 +++++++--- 3 files changed, 180 insertions(+), 97 deletions(-) diff --git a/core/oputil/oputil_file_base.js b/core/oputil/oputil_file_base.js index 4dc25fd2..efefa05b 100644 --- a/core/oputil/oputil_file_base.js +++ b/core/oputil/oputil_file_base.js @@ -209,7 +209,7 @@ function scanFileAreaForChanges(areaInfo, options, cb) { async.series( [ function quickCheck(next) { - if(!options.quick) { + if(options['full-scan']) { return next(null); } @@ -476,8 +476,8 @@ function scanFileAreas() { options.tags = tags.split(','); } - options.descFile = argv['desc-file']; // --desc-file or --desc-file PATH - options.quick = argv.quick; + options.descFile = argv['desc-file']; // --desc-file or --desc-file PATH + options['full-scan'] = argv['-full-scan']; options.areaAndStorageInfo = getAreaAndStorage(argv._.slice(2)); diff --git a/core/oputil/oputil_help.js b/core/oputil/oputil_help.js index 6f485f0a..3e9cb60a 100644 --- a/core/oputil/oputil_help.js +++ b/core/oputil/oputil_help.js @@ -9,115 +9,169 @@ exports.getHelpFor = getHelpFor; const usageHelp = exports.USAGE_HELP = { General : `usage: oputil.js [--version] [--help] - [] + [] -global args: - -c, --config PATH specify config path (${getDefaultConfigPath()}) - -n, --no-prompt assume defaults/don't prompt for input where possible +global arguments: + -c, --config PATH Specify config path (${getDefaultConfigPath()}) + -n, --no-prompt Assume defaults (don't prompt for input where possible) commands: - user user utilities - config config file management - fb file base management - mb message base management + user User management + config Configuration management + fb File base management + mb Message base management `, User : -`usage: oputil.js user [] +`usage: oputil.js user [] -actions: - info USERNAME display information about a user - pw USERNAME PASSWORD set a user's password - aliases: password, passwd - rm USERNAME permanently removes user from system - aliases: remove, delete, del - rename USERNAME NEWNAME rename a user - aliases: mv - activate USERNAME set status to active - deactivate USERNAME set status to inactive - disable USERNAME set status to disabled - lock USERNAME set status to locked - group USERNAME [+|-]GROUP adds (+) or removes (-) user from a group +Actions: + info USERNAME Display information about a user + + pw USERNAME PASSWORD Set a user's password + (passwd|password) + + rm USERNAME Permanently removes user from system + (del|delete|remove) + + rename USERNAME NEWNAME Rename a user + (mv) + + 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 : -`usage: oputil.js config [] +`usage: oputil.js config [] -actions: - new generate a new/initial configuration - cat cat current configuration to stdout +Actions: + new Generate a new / default configuration -cat args: - --no-color disable color - --no-comments strip any comments + cat Write current configuration to stdout + +cat arguments: + --no-color Disable color + --no-comments Strip any comments `, FileBase : -`usage: oputil.js fb [] +`usage: oputil.js fb [] -actions: - scan AREA_TAG[@STORAGE_TAG] scan specified area - may also contain optional GLOB as last parameter, - for example: scan some_area *.zip +Actions: + scan AREA_TAG[@STORAGE_TAG] Scan specified area - info CRITERIA display information about areas and/or files - matching CRITERIA. + May contain optional GLOB as last parameter. + Example: ./oputil.js fb scan d0pew4r3z *.zip - mv SRC [SRC...] DST move entry(s) from SRC to DST - SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG] - DST: AREA_TAG[@STORAGE_TAG] + info CRITERIA Display information about areas and/or files - rm SRC [SRC...] remove entry(s) from the system matching SRC - SRC: FILENAME_WC|SHA|FILE_ID|AREA_TAG[@STORAGE_TAG] - 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 + mv SRC [SRC...] DST Move matching entry(s) + (move) -scan args: - --tags TAG1,TAG2,... specify tag(s) to assign to discovered entries + Source may be any of the following: + - 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 - other sources such as FILE_ID.DIZ. Path must point to - a valid FILES.BBS or DESCRIPT.ION file. - --update attempt to update information for existing entries - --quick perform quick scan + rm SRC [SRC...] Remove entry(s) from the system + (del|delete|remove) -info args: - --show-desc display short description, if any + Source may be any of the following: + - Filename including '*' wildcards + - SHA-1 + - File ID + - Area tag with optional @storageTag suffix -remove args: - --phys-file also remove underlying physical file + desc CRITERIA Updates an file base entry's description -import-areas args: - --type TYPE sets import areas type. valid options are "zxx" or "na" - --create-dirs create backing storage directories + Launches an external editor using $VISUAL, $EDITOR, or vim/notepad. + + 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 : ` -general information: - AREA_TAG[@STORAGE_TAG] can specify an area tag and optionally, a storage specific tag - example: retro@bbs +General Information: + Generally an area tag can also include an optional storage tag. For example, the + area of 'bbswarez' stored using 'bbswarez_main': bbswarez@bbswarez_main - CRITERIA file base entry criteria. in general, can be AREA_TAG, SHA, - FILE_ID, or FILENAME_WC. - - FILENAME_WC filename with * and ? wildcard support. may match 0:n entries - SHA full or partial SHA-256 - FILE_ID a file identifier. see file.sqlite3 + When performing an initial import of a large area or storage backing, --full-scan + is the best option. If re-scanning an area for updates a standard / quick scan is + generally good enough. + + File ID's are those found in file.sqlite3. `, MessageBase : -`usage: oputil.js mb [] +`usage: oputil.js mb [] actions: - areafix CMD1 CMD2 ... ADDR sends an AreaFix NetMail to ADDR with the supplied command(s) - 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 + areafix CMD1 CMD2 ... ADDR Sends an AreaFix NetMail -import-areas args: - --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 comma separated uplinks - --type TYPE area import type. valid options are "bbs" and "na" + NetMail is sent to supplied address with the supplied command(s). Multi-part commands + such as "%COMPRESS ZIP" should be quoted. + + import-areas PATH Import areas using FidoNet *.NA or AREAS.BBS file + +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". ` }; diff --git a/core/oputil/oputil_user.js b/core/oputil/oputil_user.js index 77e0be68..92705bff 100644 --- a/core/oputil/oputil_user.js +++ b/core/oputil/oputil_user.js @@ -329,7 +329,7 @@ function showUserInfo(user) { return user.properties[p] || 'N/A'; }; - console.info(`User information: + const stdInfo = `User information: Username : ${user.username}${user.isRoot() ? ' (root/SysOp)' : ''} Real name : ${propOrNA(UserProps.RealName)} ID : ${user.userId} @@ -340,11 +340,29 @@ Last login : ${lastLogin()} Login count : ${propOrNA(UserProps.LoginCount)} Email : ${propOrNA(UserProps.EmailAddress)} 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) { return printUsageAndSetExitCode(getHelpFor('User'), ExitCodes.ERROR); } @@ -359,8 +377,15 @@ function twoFactorAuth(user) { function validate(callback) { // :TODO: Prompt for if not supplied 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 => { - return t.toLowerCase() === otpType; + return t.toLowerCase() === otpType.toLowerCase(); }); if(!otpType) { return callback(Errors.Invalid('Invalid OTP type')); @@ -377,7 +402,7 @@ function twoFactorAuth(user) { }); }, function storeOrDisplayQR(otpInfo, callback) { - if(!argv.out) { + if(!argv.out || !otpInfo.qr) { return callback(null, otpInfo); } @@ -400,15 +425,18 @@ function twoFactorAuth(user) { if(err) { console.error(err.message); } else { - console.info(`OTP enabled for ${user.username}.`); - console.info(`Secret: ${otpInfo.secret}`); - console.info(`Backup codes: ${otpInfo.backupCodes.join(', ')}`); + console.info(`OTP enabled for : ${user.username}`); + console.info(`Secret : ${otpInfo.secret}`); + console.info(`Backup codes : ${otpInfo.backupCodes.join(', ')}`); - if(!argv.out) { - console.info('QR code:'); - console.info(otpInfo.qr); - } else { - console.info(`QR code saved to ${argv.out}`); + if(otpInfo.qr) { + if(!argv.out) { + console.info('--- Begin QR ---'); + console.info(otpInfo.qr); + console.info('--- End QR ---'); + } else { + console.info(`QR code saved to ${argv.out}`); + } } } } @@ -429,7 +457,7 @@ function handleUserCommand() { 'pw', 'pass', 'passwd', 'password', 'group', 'mv', 'rename', - '2fa', + '2fa-otp', 'otp' ].includes(action) ? argv._.length - 2 : argv._.length - 1; const userName = argv._[usernameIdx]; @@ -465,7 +493,8 @@ function handleUserCommand() { info : showUserInfo, - '2fa' : twoFactorAuth, + '2fa-otp' : twoFactorAuthOTP, + otp : twoFactorAuthOTP, }[action] || errUsage)(user, action); }); } \ No newline at end of file