mirror of
https://github.com/NuSkooler/enigma-bbs.git
synced 2025-06-06 04:37:12 +02:00
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:
parent
6953cdf159
commit
d215919bff
3 changed files with 180 additions and 97 deletions
|
@ -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));
|
||||||
|
|
||||||
|
|
|
@ -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".
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue