Skip to content

Commit

Permalink
updated: configuration mechanism now overrides default
Browse files Browse the repository at this point in the history
  • Loading branch information
MartijnR committed Feb 23, 2015
1 parent 97549b5 commit 3f3f9b3
Show file tree
Hide file tree
Showing 17 changed files with 60 additions and 52 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public/lib/enketo-core
public/lib/martijnr-foundation
public/lib/bower-components
locales/en/translation_old.json
config/config.json
.rebooted
.vagrant
.scratchpad

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
[Unreleased]
-----------
##### Updated
- Configuration now done with local config.json that overrides default (rename existing config/config.json before updating!)
- Dutch translation

##### Fixed
Expand Down
2 changes: 1 addition & 1 deletion Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ module.exports = function( grunt ) {
var clientConfigPath = "public/temp-client-config.json";
if ( task === 'create' ) {
var config = require( './app/models/config-model' );
grunt.file.write( clientConfigPath, JSON.stringify( config.client() ) );
grunt.file.write( clientConfigPath, JSON.stringify( config.client ) );
grunt.log.writeln( 'File ' + clientConfigPath + ' created' );
} else if ( task === 'remove' ) {
grunt.file.delete( clientConfigPath );
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ A super light-weight node.js version of Enketo Smart Paper. Chock-full of [badas
3. Install libxslt and libxml2 with `(sudo) apt-get install libxml2-dev libxslt1-dev`
4. Clone this repository
5. Clone git submodules with `git submodule update --init --recursive`
6. install dependencies with `npm install` and `bower install` from the project root
7. Build with `grunt` from the project root
6. Install dependencies with `npm install` and `bower install` from the project root
7. Create a configuration file config/config.json to override values in the [default config](./config/default-config.json) with `cp config/default-config.json config/config.json`
8. Build with `grunt` from the project root

### How to install as a local VirtualBox VM - the easy way
1. Install [Vagrant](https://docs.vagrantup.com/v2/installation/index.html) and [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
Expand All @@ -25,29 +26,29 @@ _\* sometimes `vagrant up` fails for reasons beyond our control - e.g. if extern

### How to configure

All configuration is done in [config.json](./config/config.json). The configuration items have self-explanatory names and helpful sample values. After editing the configuration, the app will need to be restarted.
All configuration is done in config/config.json. Strictly speaking, this file only has to contain the [default properties](./config/default-config.json) that you'd like to override, but it might be safer to include all properties. The configuration items have self-explanatory names and helpful sample values. After editing the configuration, the app will need to be restarted.

The `maps` configuration can include an array of Mapbox TileJSON objects (or a subset of these with at least a tiles (array) and an attribution property)

The default production configuration includes 2 redis instances for the cache. You can **greatly simplify installation by using 1 redis instance** instead (for non-production usage). To do this set the redis.cache.port to 6379 (same as redis.main.port). To set up 2 instances properly for production, you'll find the vagrant setup steps in [bootstrap.sh](./setup/bootstrap.sh) useful.

To configure external authentication see [this section](#authentication).

The API is accessible on **/api/v2** (v2 is backwards-compatible with enketo-legacy's v1).
The API is accessible on **/api/v2** and **/api/v1**.

### How to run
Run with `npm start` from project root.

You can now check that the app is running by going to e.g. https://localhost:8005 (depending on your server and port set in [config.json](./config/config.json) or the port forwarding set up in Vagrant (default is 8006))
You can now check that the app is running by going to e.g. https://localhost:8005 (depending on your server and port set in config/config.json or the port forwarding set up in Vagrant (default is 8006))

For a production server, we recommend using [pm2](https://github.com/unitech/pm2) to manage the node app.
For a production server, we recommend using [pm2](https://github.com/unitech/pm2) or [forever](https://github.com/foreverjs/forever) to manage the node app.


### How to update
* update git repository with `git pull && git submodule update --init --recursive`
* update dependencies with `npm update && bower update`
* re-build with `grunt`

* restart app

### Developer tools
Install [nodemon](https://github.com/remy/nodemon) to automatically restart the server when a file changes.
Expand Down Expand Up @@ -80,7 +81,7 @@ The easiest way to start the app in development and debugging mode with liverelo

### Themes

The default theme can be set in [config.json](config/config.json). The default theme can be overridden in [the form definition](https://xlsform.org/#grid).
The default theme can be set in config/config.json. The default theme can be overridden in [the form definition](https://xlsform.org/#grid).

The recommended way to customize themes is to either:

Expand All @@ -105,7 +106,7 @@ To make use of external authentication set the following in [config.json](config

There are two potential security issues to be aware of, both of should be resolved by running this application on **https** with a valid SSL certificate.

API security is mainly arranged by the secret API key set up in [config.json](config/config.json). This API key is sent in **cleartext** to Enketo by the form/data server (such as ODK Aggregate) and can easily be intercepted and read _if the transport is not secure_. Somebody could start using your Enketo Express installation for their own form/data server, or obtain the URLs of your forms. Using secure (https) transport mitigates against this hazard. Security increases as well by populating the _server url_ in [config.json](config/config.json). Also, don't forget to change your API key when you start running Enketo Express in production.
API security is mainly arranged by the secret API key set up in config/config.json. This API key is sent in **cleartext** to Enketo by the form/data server (such as ODK Aggregate) and can easily be intercepted and read _if the transport is not secure_. Somebody could start using your Enketo Express installation for their own form/data server, or obtain the URLs of your forms. Using secure (https) transport mitigates against this hazard. Security increases as well by populating the _server url_ in config/config.json. Also, don't forget to change your API key when you start running Enketo Express in production.

Form authentication is only secure when Enketo is running on **https**. To avoid leaking form server credentials, authentication is automatically disabled when the app is accessed in a 'production' environment on 'http'. If you **have to** to run the app on http in a production environment, you can bypass this security by setting `"allow insecure transport": true` in config/config.json. The only use case this would be acceptable in is when running the app on a local protected network (e.g. in the KoBo VM).

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/config-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ module.exports = function( app ) {

router
.get( '/', function( req, res, next ) {
res.json( config.client() );
res.json( config.client );
} );
48 changes: 35 additions & 13 deletions app/models/config-model.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
"use strict";

var config = require( '../../config/config' ),
var config = require( '../../config/default-config' ),
localConfig = require( '../../config/config' ),
merge = require( 'lodash/object/merge' ),
path = require( 'path' ),
fs = require( 'fs' ),
themePath = path.join( __dirname, '../../public/css' ),
languagePath = path.join( __dirname, '../../locales' ),
debug = require( 'debug' )( 'config-model' );

module.exports = {
client: function() {
var app = require( '../../config/express' );
// merge default and local config
merge( config, localConfig );

// detect supported themes
config[ 'themes supported' ] = [];
if ( fs.existsSync( themePath ) ) {
fs.readdirSync( themePath ).forEach( function( file ) {
var matches = file.match( /^theme-([A-z]+)\.css$/ );
if ( matches && matches.length > 1 ) {
config[ 'themes supported' ].push( matches[ 1 ] );
}
} );
}

return {
googleApiKey: config.google[ 'api key' ],
maps: config.maps,
widgets: config.widgets,
modernBrowsersURL: 'modern-browsers',
supportEmail: config.support.email,
themesSupported: app.get( 'themes supported' ),
languagesSupported: app.get( 'languages supported' )
};
// detect supported languages
config[ 'languages supported' ] = fs.readdirSync( languagePath ).filter( function( file ) {
return fs.statSync( path.join( languagePath, file ) ).isDirectory();
} );

module.exports = {
server: config,
client: {
googleApiKey: config.google[ 'api key' ],
maps: config.maps,
widgets: config.widgets,
modernBrowsersURL: 'modern-browsers',
supportEmail: config.support.email,
themesSupported: config[ 'themes supported' ],
languagesSupported: config[ 'languages supported' ]
}
};
2 changes: 1 addition & 1 deletion app/models/instance-model.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use strict";

var Q = require( 'q' ),
config = require( '../../config/config' ),
config = require( './config-model' ).server,
TError = require( '../lib/custom-error' ).TranslatedError,
utils = require( '../lib/utils' ),
client = require( 'redis' ).createClient( config.redis.main.port, config.redis.main.host, {
Expand Down
2 changes: 1 addition & 1 deletion app/models/manifest-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ var libxml = require( "libxmljs" ),
path = require( 'path' ),
fs = require( 'fs' ),
Q = require( 'q' ),
config = require( '../../config/config' ),
config = require( './config-model' ).server,
client = require( 'redis' ).createClient( config.redis.cache.port, config.redis.cache.host, {
auth_pass: config.redis.cache.password
} ),
Expand Down
2 changes: 1 addition & 1 deletion app/models/survey-model.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
var Q = require( 'q' ),
utils = require( '../lib/utils' ),
TError = require( '../lib/custom-error' ).TranslatedError,
config = require( '../../config/config' ),
config = require( './config-model' ).server,
client = require( 'redis' ).createClient( config.redis.main.port, config.redis.main.host, {
auth_pass: config.redis.main.password
} ),
Expand Down
File renamed without changes.
21 changes: 1 addition & 20 deletions config/express.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ var themesSupported = [],
cookieParser = require( 'cookie-parser' ),
fs = require( 'fs' ),
favicon = require( 'serve-favicon' ),
config = require( './config' ),
config = require( '../app/models/config-model' ).server,
logger = require( 'morgan' ),
i18n = require( 'i18next' ),
compression = require( 'compression' ),
errorHandler = require( '../app/controllers/error-handler' ),
controllersPath = path.join( __dirname, '../app/controllers' ),
themePath = path.join( __dirname, '../public/css' ),
languagePath = path.join( __dirname, '../locales' ),
app = express(),
debug = require( 'debug' )( 'express' );

Expand All @@ -34,23 +32,6 @@ app.set( 'view engine', 'jade' );
// pretty json API responses
app.set( 'json spaces', 4 );

// detect supported themes
if ( fs.existsSync( themePath ) ) {
fs.readdirSync( themePath ).forEach( function( file ) {
var matches = file.match( /^theme-([A-z]+)\.css$/ );
if ( matches && matches.length > 1 ) {
themesSupported.push( matches[ 1 ] );
}
} );
}
app.set( 'themes supported', themesSupported );

// detect supported languages
languagesSupported = fs.readdirSync( languagePath ).filter( function( file ) {
return fs.statSync( path.join( languagePath, file ) ).isDirectory();
} );
app.set( 'languages supported', languagesSupported );

// setup i18next
i18n.init( {
// don't bother with these routes
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"jade": "1.x.x",
"jwt-simple": "^0.2.0",
"libxmljs": "0.13.x",
"lodash": "3.3.x",
"morgan": "1.x.x",
"node_xslt": "0.1.x",
"q": "2.0.x",
Expand Down
2 changes: 1 addition & 1 deletion test/server/account-model.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var model,
chaiAsPromised = require( "chai-as-promised" ),
app = require( '../../config/express' ),
model = require( '../../app/models/account-model' ),
config = require( "../../config/config" );
config = require( "../../app/models/config-model" ).server;

chai.use( chaiAsPromised );

Expand Down
2 changes: 1 addition & 1 deletion test/server/cache-model.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var model, survey,
expect = chai.expect,
chaiAsPromised = require( "chai-as-promised" ),
redis = require( "redis" ),
config = require( "../../config/config" ),
config = require( "../../app/models/config-model" ).server,
client = redis.createClient( config.redis.cache.port, config.redis.cache.host, {
auth_pass: config.redis.cache.password
} ),
Expand Down
2 changes: 1 addition & 1 deletion test/server/instance-model.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var model,
expect = chai.expect,
chaiAsPromised = require( "chai-as-promised" ),
model = require( '../../app/models/instance-model' ),
config = require( "../../config/config" );
config = require( "../../app/models/config-model" ).server;

chai.use( chaiAsPromised );

Expand Down
2 changes: 1 addition & 1 deletion test/server/submission-controller.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var Q = require( "q" ),
surveyModel = require( '../../app/models/survey-model' ),
instanceModel = require( '../../app/models/instance-model' ),
redis = require( "redis" ),
config = require( "../../config/config" ),
config = require( "../../app/models/config-model" ).server,
client = redis.createClient( config.redis.main.port, config.redis.main.host, {
auth_pass: config.redis.main.password
} );
Expand Down
2 changes: 1 addition & 1 deletion test/server/survey-model.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var model,
expect = chai.expect,
chaiAsPromised = require( "chai-as-promised" ),
redis = require( "redis" ),
config = require( "../../config/config" ),
config = require( "../../app/models/config-model" ).server,
model = require( '../../app/models/survey-model' ),
client = redis.createClient( config.redis.main.port, config.redis.main.host, {
auth_pass: config.redis.main.password
Expand Down

0 comments on commit 3f3f9b3

Please sign in to comment.