diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd6542f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +# From .gitignore +.idea/ +node_modules/ +config/config.json + +# Others +.git +.gitignore +.DS_Store +.dockerignore +Dockerfile +README.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0de4036 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# Example Docker Usage: +# docker build -t ldap-jwt . +# docker run -p 3000:3000 --rm -it -v "$(pwd)/config/config.test.json:/usr/src/app/config/config.json" ldap-jwt + +FROM node:6.9.2 + +ENV LDAPJWT_BASE_DIR="/usr/src/app" +EXPOSE 3000 + +WORKDIR "${LDAPJWT_BASE_DIR}" + +# Load dependencies to optimize the build cache +COPY package.json ./ +RUN npm install + +#Copy code +COPY . ./ + +CMD [ "npm", "start" ] diff --git a/config/config.test.json b/config/config.test.json index 74f14ba..602b3e1 100644 --- a/config/config.test.json +++ b/config/config.test.json @@ -5,9 +5,10 @@ "searchBase": "dc=example,dc=com", "searchFilter": "(uid={{username}})", "timeout": 5000, - "connectTimeout": 10000 + "connectTimeout": 10000, + "reconnect": true }, "jwt": { "secret": "test_secret" } -} \ No newline at end of file +} diff --git a/index.js b/index.js index 88ab2e7..014021c 100644 --- a/index.js +++ b/index.js @@ -30,8 +30,8 @@ var authenticate = function (username, password) { }; app.post('/authenticate', function (req, res) { - if(req.body.username && req.body.password) { - authenticate(req.body.username, req.body.password) + if(req.body.username && req.body.password) { + authenticate(req.body.username, req.body.password) .then(function(user) { var expires = moment().add(2, 'days').valueOf(); var token = jwt.encode({ @@ -44,12 +44,29 @@ app.post('/authenticate', function (req, res) { res.json({token: token, full_name: user.cn}); }) .catch(function (err) { + // Ldap reconnect config needs to be set to true to reliably + // land in this catch when the connection to the ldap server goes away. + // REF: https://github.com/vesse/node-ldapauth-fork/issues/23#issuecomment-154487871 + console.log(err); - res.status(401).send({ error: 'Wrong user or password'}); + + if (err.name === 'InvalidCredentialsError' || (typeof err === 'string' && err.match(/no such user/i)) ) { + res.status(401).send({ error: 'Wrong user or password'}); + } else { + // ldapauth-fork or underlying connections may be in an unusable state. + // Reconnect option does re-establish the connections, but will not + // re-bind. Create a new instance of LdapAuth. + // REF: https://github.com/vesse/node-ldapauth-fork/issues/23 + // REF: https://github.com/mcavage/node-ldapjs/issues/318 + + res.status(500).send({ error: 'Unexpected Error'}); + auth = new LdapAuth(settings.ldap); + } + }); - } else { - res.status(400).send({error: 'No username or password supplied'}); - } + } else { + res.status(400).send({error: 'No username or password supplied'}); + } }); app.post('/verify', function (req, res) { @@ -78,6 +95,9 @@ app.post('/verify', function (req, res) { var port = (process.env.PORT || 3000); app.listen(port, function() { - console.log('Listening on port: ' + port); -}); + console.log('Listening on port: ' + port); + if (typeof settings.ldap.reconnect === 'undefined' || settings.ldap.reconnect === null || settings.ldap.reconnect === false) { + console.warn('WARN: This service may become unresponsive when ldap reconnect is not configured.') + } +}); diff --git a/package.json b/package.json index 4ae363a..37dfda1 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Lightweight node.js based web service that provides user authentication against LDAP server (Active Directory / Windows network) credentials and returns a JSON Web Token.", "main": "index.js", "scripts": { + "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [