9

I'm writing a nodejs cli utility (NPM module intended to be installed globally) that will need to store some user-supplied values. What is the best way to store these values on a system?

For example, should I create my own config file under say: /etc/{my-utility-name}/conf.json and have this directory+file initialized in my install script

(https://npmjs.org/doc/misc/npm-scripts.html)

Casey Flynn
  • 13,654
  • 23
  • 103
  • 194

4 Answers4

6

Looking at the questions and comments i think there are two problems to solve:

  1. Where to save it: If there are user specific settings, then by common pattern in Linux its best saved in the hidden directory in user's home directory. Hence you can best keep it in directory $HOME/.{yourAppName}/someFile

    E.x: Pidgin saves it in: /home/kushal/.purple/...
    ("." prefix to folder name makes it hidden)
       
    You can save it in: /home/$HOME/.myawesomeNPMModule/...

    NOTE: If you are also targeting the windows platform, then you need a platform check and decide which path to use.

    In windows Vista and above it goes in
    $WIN_INSTALLATION_DRIVE\Users\$USER_NAME\AppData\Local\YourAppName...
    In windows XP it goes in 
    $WIN_INSTALLATION_DRIVE\Documents and Settings\$USER_NAME\Local Settings\Application Data\Local\YourAppName\...

    Yes cross platform makes life difficult, or atleast adds few IF statements. ;)

  2. How to save it: You can save the credentials in JSON file easily. Now as you need the data to be secured and also not read by illegal access, the best way is to encrypt it. NodeJs Crypto module is very easy to use and you can use it in few lines to encry/decrypt your settings file. Your application will only be aware of encryption passwords and hence your application will only be able to decrypt it.

    I would recommend AES-192 for encrypting the files.

Hope it helps!!

Kushal Likhi
  • 572
  • 3
  • 7
3

I found that the yeoman project has a package for this called configstore. I tried it out and the API is incredibly simple and easy to use and abstracts out any need to worry about OS compatibility.

Getting and setting configs is as easy as:

const Configstore = require('configstore')
const config = Configstore(packageName, defaults)
config.set(key, value)
config.get(key)

Yeoman is a large well tested project so the fact that they built and use this in their project should indicate that this is a safe stable package to use in your own project.

TimE
  • 2,800
  • 1
  • 27
  • 25
1

If you have a limited number of settings, a JSON file will do. If you have a more elaborate set or configs, consider using something like a SQLite file, for easier retrieval.

At any rate, save that file in a directory that does not require elevated permissions. Also, if it's unique to the user, maybe consider saving the file under the user's home directory.

Traveling Tech Guy
  • 27,194
  • 23
  • 111
  • 159
  • $HOME would work. This script may hold on to usernames&passwords to 3rd party services (github, bitbucket...) What do you think? 'Hell-no' on storing passwords in plaintext? – Casey Flynn Oct 18 '13 at 05:08
  • @CaseyFlynn Obviously! In linux passwords are salted and hashed, they are never stored in plaintext. You should also do the same. – user568109 Oct 18 '13 at 05:57
  • @user568109 -- if only store it's md5 or sha hash equivalent, the module can't use it later to interface with 3rd party services on behalf of the user. So that would defeat the point. – Casey Flynn Oct 18 '13 at 06:20
  • @CaseyFlynn OK in that case encrypt it with SHA and store it. – user568109 Oct 18 '13 at 06:54
  • @user568109, I think you mean use a symmetric encrpytion algorithm. SHA1 isn't encryption, it's a 1-way hashing algorithm. For the purposes of holding a user's access credentials to a service to interface with that service on the user's behalf, SHA1 is useless because it is basically impossible to take a hash and revert it to it's original, pre-hash value. And the pre-hash value would be what is necessary to interface with a 3rd party service. http://stackoverflow.com/a/990711/480807 – Casey Flynn Oct 18 '13 at 07:05
  • @CaseyFlynn Yeah, mentioning SHA was a mistake, I meant using DES/AES or the likes. You can encrypt it then convert to base64 encoding, so that you can store it as a string in JSON. – user568109 Oct 18 '13 at 07:36
  • 1
    But how is is this supposed to be secure? where should the key be located then? – vkurchatkin Oct 18 '13 at 08:29
1

I wrote the cli-config API to manage NodeJS app configuration including:

  • Package defaults
  • Local user config
  • Command line options
  • App overrides

Visit https://github.com/tohagan/cli-config for more details.

Example 1:

Combines configuration options from package file defaults.config then ~/.<appname>.config then command line options then force the debug option to be true. Uses a shallow merge so only the top level properties are merged.

var config = require('../cli-config')
             .getConfig({dirname: __dirname, override: {debug: true}});

Example 2:

Deep merge nested configuration settings from package defaults.config then ./config.json then command line options. If ./config.json does not exist, clone a copy from defaults.config so the user can use it to override defaults.config in the future.

var config = require('../cli-config').getConfig({
    dirname: __dirname,          
    clone: true,                 
    configFile: './config.json', 
    merge: 'deep'                
});

Example 3:

The command line parser returns an object that can be used to override the system settings or user settings options. You can configure this parser using the cli option. Refer to minimist for more details about command line parsing options.

var config = require('../cli-config').getConfig({
    dirname: __dirname,
    cli: { 
        boolean: {
            'd': 'debug',
            'v': 'verbose'
        } 
    } 
});

Sets the config.debug and config.verbose options to true.

$ myapp -d -v  
Tony O'Hagan
  • 21,638
  • 3
  • 67
  • 78
  • I had a similar question but don't see how `cli-config` works to store values, it seems to be only for reading configs, not for writing them. Probably by now there's a clean solution, but came across this in my search. – Bernie B Oct 29 '19 at 15:18
  • Yes. This API is read only, but its easy to write a config file that this API will then pick up - while still allowing you to override settings. – Tony O'Hagan Nov 02 '19 at 00:00