From c7d6151d6c2556153d2ef287884a2275c46aca96 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 11:11:27 -0500 Subject: [PATCH 01/19] initial --- .gitignore | 5 + .jscsrc | 82 +++++++++++++ .jshintrc | 19 ++++ .travis.yml | 10 ++ .vscode/tasks.json | 190 +++++++++++++++++++++++++++++++ LICENSE | 21 ++++ README.md | 2 + lib/acl-example.js | 30 +++++ lib/index.js | 67 +++++++++++ lib/pouchdb.js | 37 ++++++ package.json | 40 +++++++ sample/key-cert.pem | 15 +++ sample/key.pem | 15 +++ sample/package.json | 18 +++ sample/runit.sh | 3 + sample/server.js | 29 +++++ test/acl-get-multipleusers.js | 31 +++++ test/acl-get-user.js | 25 ++++ test/handlers.js | 19 ++++ test/jshint.spec.js | 5 + test/test-acl-allfail-nopath.js | 180 +++++++++++++++++++++++++++++ test/test-acl-fatfinger.js | 126 ++++++++++++++++++++ test/test-acl-get-user.js | 180 +++++++++++++++++++++++++++++ test/test-acl-loopquerystring.js | 105 +++++++++++++++++ test/test-acl-publicall.js | 69 +++++++++++ test/test-acl-querystrings.js | 126 ++++++++++++++++++++ test/test-acl-userall.js | 126 ++++++++++++++++++++ 27 files changed, 1575 insertions(+) create mode 100644 .jscsrc create mode 100644 .jshintrc create mode 100644 .travis.yml create mode 100644 .vscode/tasks.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 lib/acl-example.js create mode 100644 lib/index.js create mode 100644 lib/pouchdb.js create mode 100644 package.json create mode 100644 sample/key-cert.pem create mode 100644 sample/key.pem create mode 100644 sample/package.json create mode 100755 sample/runit.sh create mode 100644 sample/server.js create mode 100644 test/acl-get-multipleusers.js create mode 100644 test/acl-get-user.js create mode 100644 test/handlers.js create mode 100644 test/jshint.spec.js create mode 100644 test/test-acl-allfail-nopath.js create mode 100644 test/test-acl-fatfinger.js create mode 100644 test/test-acl-get-user.js create mode 100644 test/test-acl-loopquerystring.js create mode 100644 test/test-acl-publicall.js create mode 100644 test/test-acl-querystrings.js create mode 100644 test/test-acl-userall.js diff --git a/.gitignore b/.gitignore index e920c16..868c657 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,8 @@ node_modules # Optional REPL history .node_repl_history + + +# pouchdb +db/* +sample/db/* \ No newline at end of file diff --git a/.jscsrc b/.jscsrc new file mode 100644 index 0000000..020b255 --- /dev/null +++ b/.jscsrc @@ -0,0 +1,82 @@ +{ + "disallowUnusedParams": true, + "jsDoc": { + "checkAnnotations": "jsdoc3", + "checkParamExistence": true, + "checkParamNames": true, + "checkTypes": "strictNativeCase", + "requireParamTypes": true, + "checkRedundantAccess": "enforceLeadingUnderscore", + "checkRedundantParams": true, + "checkRedundantReturns": true, + "checkReturnTypes": true, + "leadingUnderscoreAccess": true, + "requireDescriptionCompleteSentence": true, + "requireReturnTypes": true + }, + "maximumLineLength": { + "value": 80, + "allExcept": ["regex", "urlComments", "require"] + }, + "maximumNumberOfLines": 1000, + "requireCamelCaseOrUpperCaseIdentifiers": true, + "requireCapitalizedConstructors": true, + "requireCommaBeforeLineBreak": true, + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch", + "case", + "default" + ], + "requireDotNotation": { "allExcept": [ "keywords" ] }, + "requireOperatorBeforeLineBreak": [ + "?", + "=", + "+", + "-", + "/", + "*", + "==", + "===", + "!=", + "!==", + ">", + ">=", + "<", + "<=" + ], + "requireSemicolons": true, + "requireSpaceAfterKeywords": [ + "do", + "for", + "if", + "else", + "switch", + "case", + "try", + "catch", + "void", + "while", + "with", + "return", + "typeof", + "function" + ], + "requireSpaceAfterComma": true, + "requireSpaceAfterLineComment": true, + "requireSpaceBetweenArguments": true, + "requireSpacesInConditionalExpression": { + "afterTest": true, + "beforeConsequent": true, + "afterConsequent": true, + "beforeAlternate": true + }, + "safeContextKeyword": ["self"], + "validateIndentation": 2, + "validateQuoteMarks": "'" +} diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..129bd89 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,19 @@ +{ + "eqeqeq": true, + "noarg": true, + "undef": true, + "unused": "vars", + "eqnull": true, + "node": true, + "strict": "global", + "nonbsp": true, + "forin": true, + "futurehostile": true, + "expr": true, + "nonew": true, + "singleGroups": true, + "latedef": true, + "freeze": true, + "maxdepth": 3, + "esversion": 6 +} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..de22f9b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +node_js: + - stable + - "4.1" + - "0.12" + - "0.11" +os: + - linux +script: "npm run-script test-travis" +after_script: "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..1f31215 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,190 @@ +// Available variables which can be used inside of strings. +// ${workspaceRoot}: the root folder of the team +// ${file}: the current opened file +// ${fileBasename}: the current opened file's basename +// ${fileDirname}: the current opened file's dirname +// ${fileExtname}: the current opened file's extension +// ${cwd}: the current working directory of the spawned process + +// A task runner that calls the Typescript compiler (tsc) and +// Compiles a HelloWorld.ts program +{ + "version": "0.1.0", + + // The command is tsc. Assumes that tsc has been installed using npm install -g typescript + "command": "tsc", + + // The command is a shell script + "isShellCommand": true, + + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // args is the HelloWorld program to compile. + "args": ["HelloWorld.ts"], + + // use the standard tsc problem matcher to find compile problems + // in the output. + "problemMatcher": "$tsc" +} + +// A task runner that calls the Typescript compiler (tsc) and +// compiles based on a tsconfig.json file that is present in +// the root of the folder open in VSCode +/* +{ + "version": "0.1.0", + + // The command is tsc. Assumes that tsc has been installed using npm install -g typescript + "command": "tsc", + + // The command is a shell script + "isShellCommand": true, + + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + + // Tell the tsc compiler to use the tsconfig.json from the open folder. + "args": ["-p", "."], + + // use the standard tsc problem matcher to find compile problems + // in the output. + "problemMatcher": "$tsc" +} +*/ + +// A task runner configuration for gulp. Gulp provides a less task +// which compiles less to css. +/* +{ + "version": "0.1.0", + "command": "gulp", + "isShellCommand": true, + "tasks": [ + { + "taskName": "less", + // Make this the default build command. + "isBuildCommand": true, + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the standard less compilation problem matcher. + "problemMatcher": "$lessCompile" + } + ] +} +*/ + +// Uncomment the following section to use jake to build a workspace +// cloned from https://github.com/Microsoft/TypeScript.git +/* +{ + "version": "0.1.0", + // Task runner is jake + "command": "jake", + // Need to be executed in shell / cmd + "isShellCommand": true, + "showOutput": "silent", + "tasks": [ + { + // TS build command is local. + "taskName": "local", + // Make this the default build command. + "isBuildCommand": true, + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the redefined Typescript output problem matcher. + "problemMatcher": [ + "$tsc" + ] + } + ] +} +*/ + +// Uncomment the section below to use msbuild and generate problems +// for csc, cpp, tsc and vb. The configuration assumes that msbuild +// is available on the path and a solution file exists in the +// workspace folder root. +/* +{ + "version": "0.1.0", + "command": "msbuild", + "args": [ + // Ask msbuild to generate full paths for file names. + "/property:GenerateFullPaths=true" + ], + "taskSelector": "/t:", + "showOutput": "silent", + "tasks": [ + { + "taskName": "build", + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the standard MS compiler pattern to detect errors, warnings + // and infos in the output. + "problemMatcher": "$msCompile" + } + ] +} +*/ + +// Uncomment the following section to use msbuild which compiles Typescript +// and less files. +/* +{ + "version": "0.1.0", + "command": "msbuild", + "args": [ + // Ask msbuild to generate full paths for file names. + "/property:GenerateFullPaths=true" + ], + "taskSelector": "/t:", + "showOutput": "silent", + "tasks": [ + { + "taskName": "build", + // Show the output window only if unrecognized errors occur. + "showOutput": "silent", + // Use the standard MS compiler pattern to detect errors, warnings + // and infos in the output. + "problemMatcher": [ + "$msCompile", + "$lessCompile" + ] + } + ] +} +*/ +// A task runner example that defines a problemMatcher inline instead of using +// a predefined one. +/* +{ + "version": "0.1.0", + "command": "tsc", + "isShellCommand": true, + "args": ["HelloWorld.ts"], + "showOutput": "silent", + "problemMatcher": { + // The problem is owned by the typescript language service. Ensure that the problems + // are merged with problems produced by Visual Studio's language service. + "owner": "typescript", + // The file name for reported problems is relative to the current working directory. + "fileLocation": ["relative", "${cwd}"], + // The actual pattern to match problems in the output. + "pattern": { + // The regular expression. Matches HelloWorld.ts(2,10): error TS2339: Property 'logg' does not exist on type 'Console'. + "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", + // The match group that denotes the file containing the problem. + "file": 1, + // The match group that denotes the problem location. + "location": 2, + // The match group that denotes the problem's severity. Can be omitted. + "severity": 3, + // The match group that denotes the problem code. Can be omitted. + "code": 4, + // The match group that denotes the problem's message. + "message": 5 + } + } +} +*/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6b66b96 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Shawn Cicoria + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1f6136d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# SALTI +[![Build Status](https://travis-ci.org/cicorias/salt2.svg?branch=master)](https://travis-ci.org/cicorias/salt2) diff --git a/lib/acl-example.js b/lib/acl-example.js new file mode 100644 index 0000000..ad95540 --- /dev/null +++ b/lib/acl-example.js @@ -0,0 +1,30 @@ +'use strict'; + +module.exports = [{ + path: '/', + roles: [{ + role: 'public', + verbs: ['get'] + }, { + role: 'user', + verbs: ['get'] + }] +}, { + path: '/foo', + roles: [{ + role: 'public', + verbs: ['get', 'post'] + }, { + role: 'user', + verbs: ['get', 'put', 'post'] + }] +}, { + path: '/bar', + roles: [{ + role: 'public', + verbs: ['get'] + }, { + role: 'user', + verbs: ['get'] + }] +}]; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..6b9c2d9 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,67 @@ +'use strict'; + +var debug = require('debug')('thalisalti:acl'); + +var anonymous = 'public'; + +function mapIt(source, key, match) { + return source.map(function (el) { + debug('\t %s', el); + debug(el[key]); + if (el[key]) + return el[key]; + else + return el; + }).indexOf(match); + //return rv; +} + +module.exports = function (acl) { + //path.role.verb + let msg401 = { success: false, message: 'Unauthorized' }; + + return function (req, res, next) { + debug('method: %s url: %s path: %s', req.method, req.url, req.path); + + //do a match with a slash on the end... + //if that fails do a match that is lastindex of... + + + //using headers for validation of the acl engine + let identity = req.connection.pskIdentity || anonymous; + + debug('Identity: %s', identity); + + var pathExists = mapIt(acl, 'path', req.path); + debug('req.path: %s', req.path); + if (!~pathExists) { + let justPath = req.path.substring(0, req.path.lastIndexOf("/") + 1); + debug('justpath: %s', justPath); + pathExists = mapIt(acl, 'path', justPath); + if (!~pathExists){ + debug('unauthorized1: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } + } + + let roles = acl[pathExists].roles; + let roleExists = mapIt(roles, 'role', identity); + if (!~roleExists) { + debug('unauthorized2: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } + + let verbs = acl[pathExists].roles[roleExists].verbs; + let verbExists = mapIt(verbs, 'verb', req.method.toLowerCase()); + + if (!~verbExists) { + debug('unauthorized3: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } + + //all good... + next(); + }; +}; + + diff --git a/lib/pouchdb.js b/lib/pouchdb.js new file mode 100644 index 0000000..3781de6 --- /dev/null +++ b/lib/pouchdb.js @@ -0,0 +1,37 @@ +'use strict'; + +module.exports = [{ + path: '/_utils/', + roles: [{ + role: 'public', + verbs: ['get'] + }, { + role: 'user', + verbs: ['get'] + }] +}, { + path: '/db/', + roles: [{ + role: 'public', + verbs: ['get'] + }] +}, +{ + path: '/_utils/css/', + roles: [{ + role: 'public', + verbs: ['get'] + }] +}, +{ + path: '/_utils/js/', + roles: [{ + role: 'public', + verbs: ['get'] + }] +}]; + + + +//db_utils +//db/css/ \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b3d7d10 --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "name": "salti", + "version": "0.1.1", + "description": "Simple Authentication for Thali IoT", + "main": "lib/index.js", + "scripts": { + "test": "node --harmony ./node_modules/mocha/bin/mocha", + "test-travis": "node --harmony ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha -- -R spec ./test/*" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/thaliproject/salti.git" + }, + "keywords": [ + "thali", + "thaliproject", + "pouchdb", + "salti" + ], + "author": "shawn cicoria", + "license": "MIT", + "bugs": { + "url": "https://github.com/thaliproject/salti/issues" + }, + "homepage": "https://github.com/thaliproject/salti#readme", + "dependencies": { + "debug": "^2.2.0", + "secure-compare": "^3.0.1" + }, + "devDependencies": { + "colors": "^1.1.2", + "coveralls": "^2.11.8", + "express": "^4.13.4", + "istanbul": "^0.4.2", + "jshint": "^2.9.1", + "mocha": "^2.4.5", + "mocha-jshint": "^2.3.1", + "supertest": "^1.2.0" + } +} diff --git a/sample/key-cert.pem b/sample/key-cert.pem new file mode 100644 index 0000000..e691bb3 --- /dev/null +++ b/sample/key-cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICYTCCAcoCCQDNbXkHjip2wTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV +UzELMAkGA1UECAwCTkoxETAPBgNVBAcMCERlbnZpbGxlMQswCQYDVQQKDAJNZTEL +MAkGA1UECwwCTWUxEjAQBgNVBAMMCWxvY2FsaG9zdDEYMBYGCSqGSIb3DQEJARYJ +bm9Abm8uY29tMB4XDTE2MDIxODE1MjM1M1oXDTQzMDcwNTE1MjM1M1owdTELMAkG +A1UEBhMCVVMxCzAJBgNVBAgMAk5KMREwDwYDVQQHDAhEZW52aWxsZTELMAkGA1UE +CgwCTWUxCzAJBgNVBAsMAk1lMRIwEAYDVQQDDAlsb2NhbGhvc3QxGDAWBgkqhkiG +9w0BCQEWCW5vQG5vLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzRIA +A0WTMwDRePNQgBHj0+M729g0tmoR8fcmGB0WgxXtSu17wTIz9ShrGJqSuhmKDwP1 +Ga5A5lLWDwkzuDd4V31sOcXtRLHRqsxa9zMdQuN8e98B4sDHaA4qdYny18esHPR0 +MDzEM8LrQQo2tcoS2JjLQo8WR269DbbFGE8XUTsCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQBE+dTINUyZqL0bpya3dQ12U7KMPPTXP+EeBLiTBwskp9d0L1iyPEKMPSr/ +o9X24FpGRJhaHuDxIKaqQTwiWuop8VjKiExwUQ/BHZabL+ucHa65dSO4E0SvlVN5 +9EE79fx11oqpxFEtHYRAjkURIllvndfhGmS9ixZ+tsOtgu5QKA== +-----END CERTIFICATE----- diff --git a/sample/key.pem b/sample/key.pem new file mode 100644 index 0000000..3b46274 --- /dev/null +++ b/sample/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDNEgADRZMzANF481CAEePT4zvb2DS2ahHx9yYYHRaDFe1K7XvB +MjP1KGsYmpK6GYoPA/UZrkDmUtYPCTO4N3hXfWw5xe1EsdGqzFr3Mx1C43x73wHi +wMdoDip1ifLXx6wc9HQwPMQzwutBCja1yhLYmMtCjxZHbr0NtsUYTxdROwIDAQAB +AoGBALBm1nn6BHhwMMDBVLMGMJvZMWmYnyd3ML6aojtF8lKKrxewF6+F9unhfdap +SKQrSfZGqjUQDyXRhe9jNAk4NhxKMgCt6XUWiU1djLj59G33/319l2yZmw0+/28D +tMOEgR1qpgnDBC2+3GiOuqC2NHm5voUV7prQoZxSGPP+BdHhAkEA6eYoMPzADy1T +SiDjaEmK0MXbLAIgU12VIaWNSlNYaKJfeMHYuUM29Wj6kE9HjYlijSchGkjZ1fpw +2kKtkK3oywJBAOByf1kD/ppEPHMODw62o4E/sjooiqCQpCfc2BF4GBbNGYkIFDhW +LQTmSOoB4UsFnztFUcsZ9YWhqc8jsgdh21ECQQCXVpgCukb6ivUtLYJ2Y78xvhNF +skqAJjjsAFYfFTZK3WSwmZxEorzYpQgaf3Fons/pTLwaabCoxtZmImZzClNpAkEA +wYE1VdNh0DKAQ1mdlbvUn3wA9q85RVEulceK6RS3AAF3wHKcrC1vxsebmVRhnkKM +k5ch+4635KwKMLG/rSmq4QJAZLW9dPqeGhKnEY/iwLkU+pvB4cmrTui4V78hM4Se +goXkAtU+ArzaDUmbjyyohROttVUqwuhfndsfAir4KwFMjA== +-----END RSA PRIVATE KEY----- diff --git a/sample/package.json b/sample/package.json new file mode 100644 index 0000000..b958415 --- /dev/null +++ b/sample/package.json @@ -0,0 +1,18 @@ +{ + "name": "sample", + "version": "0.1.0", + "description": "Sample App to demonstrate Salti", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "author": "Shawn Cicoria", + "license": "MIT", + "dependencies": { + "debug": "^2.2.0", + "express": "^4.13.4", + "express-pouchdb": "^1.0.2", + "pouchdb": "^5.2.1" + } +} diff --git a/sample/runit.sh b/sample/runit.sh new file mode 100755 index 0000000..9649e09 --- /dev/null +++ b/sample/runit.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +nodemon -i node_modules/ diff --git a/sample/server.js b/sample/server.js new file mode 100644 index 0000000..f26ca4d --- /dev/null +++ b/sample/server.js @@ -0,0 +1,29 @@ +/* jshint node: true */ +'use strict'; + +var express = require('express'), + app = express(), + PouchDB = require('pouchdb'), + router = express.Router(); + +var pbsetup = PouchDB.defaults({ prefix: './db/' }); + +var opts = { + mode: 'minimumForPouchDB', + overrideMode: { + include: ['routes/fauxton'] + } +} + +var acllib = require('../lib/index'); +var acl = require('../lib/pouchdb'); +//Norml middleware usage.. +router.all('*', acllib(acl)); + +var pouchApp = require('express-pouchdb')(pbsetup, opts); + +router.use('/', pouchApp); +app.use('/', router); + +app.listen(process.env.PORT || 3000); + diff --git a/test/acl-get-multipleusers.js b/test/acl-get-multipleusers.js new file mode 100644 index 0000000..2b6033e --- /dev/null +++ b/test/acl-get-multipleusers.js @@ -0,0 +1,31 @@ +'use strict'; + +module.exports = [ + { + path: '/publicall', + roles: [ + {role: 'public', + verbs: ['get', 'post', 'put']}, + {role: 'user', + verbs: ['get']} + ] + }, + { + path: '/fatfinger', + roles: [ + {role: 'public', + verbs: []}, + {role: 'user', + verbs: ['post', 'put', 'get', 'get', 'put', 'post']} + ] + }, + { + path: '/publicget', + roles: [ + {role: 'public', + verbs: ['get']}, + {role: 'user', + verbs: ['get', 'post', 'put']} + ] + } +]; diff --git a/test/acl-get-user.js b/test/acl-get-user.js new file mode 100644 index 0000000..f48994e --- /dev/null +++ b/test/acl-get-user.js @@ -0,0 +1,25 @@ +'use strict'; + +module.exports = [ + { + path: '/', + roles: [ + {role: 'user', + verbs: ['get', 'put', 'post']} + ] + }, + { + path: '/foo', + roles: [ + {role: 'user', + verbs: ['get', 'put', 'post']} + ] + }, + { + path: '/bar', + roles: [ + {role: 'user', + verbs: ['get']} + ] + } +]; diff --git a/test/handlers.js b/test/handlers.js new file mode 100644 index 0000000..af665a4 --- /dev/null +++ b/test/handlers.js @@ -0,0 +1,19 @@ +'use strict'; + +var colors = require('colors'); + +module.exports = + +[function (req, res) { + console.log(colors.green('\tin the handler for get')); + res.status(200).json({ message: 'you made it' }); + // next(); +}, function (req, res) { + console.log(colors.green('\tin the post handler')); + res.status(200).json({ name: 'you made it' }); + // next(); +}, function (req, res) { + console.log(colors.green('\tin the put handler')); + res.status(200).json({ name: 'you made it' }); + // next(); +}]; diff --git a/test/jshint.spec.js b/test/jshint.spec.js new file mode 100644 index 0000000..e52bf01 --- /dev/null +++ b/test/jshint.spec.js @@ -0,0 +1,5 @@ +require('mocha-jshint')({ + paths: [ + './lib/' + ] +}); \ No newline at end of file diff --git a/test/test-acl-allfail-nopath.js b/test/test-acl-allfail-nopath.js new file mode 100644 index 0000000..81b9086 --- /dev/null +++ b/test/test-acl-allfail-nopath.js @@ -0,0 +1,180 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +//path.verb.role +var acl = require('./acl-get-multipleusers.js'); + +describe('no paths OK all should fail', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity= 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); + + }) + + describe('should throw a 401', function () { + it('when no path exists', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + }) +}) + +describe('acl get user tests - with empty identity', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = ''; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); + + }) + + describe('should throw a 401', function () { + it('when no path exists', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + }) +}) + + + +describe('acl get user tests - with good identity', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); + + }) + + describe('should throw a 401', function () { + it('when no path exists', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + }) + +}); diff --git a/test/test-acl-fatfinger.js b/test/test-acl-fatfinger.js new file mode 100644 index 0000000..483443a --- /dev/null +++ b/test/test-acl-fatfinger.js @@ -0,0 +1,126 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +//path.verb.role +var acl = require('./acl-get-multipleusers.js'); + + +describe('fatfinger - user - mixed and duplicate', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/fatfinger', handlers[0]); + router.post('/fatfinger', handlers[1]); + router.put('/fatfinger', handlers[2]); + app.use('/', router); + + }) + + describe('fatfinger verbs', function () { + it('shoudl be OK - 200', function (done) { + request(app) + .get('/fatfinger') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .put('/fatfinger') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .post('/fatfinger') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + }) + +}) + +describe('fatfinger - public - no public all', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity= 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/fatfinger', handlers[0]); + router.post('/fatfinger', handlers[1]); + router.put('/fatfinger', handlers[2]); + app.use('/', router); + + }) + + describe('fatfinger - public no verbs', function () { + it('should be 401', function (done) { + request(app) + .get('/fatfinger') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('should be 401', function (done) { + request(app) + .put('/fatfinger') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('should be 401', function (done) { + request(app) + .post('/fatfinger') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + }) + +}) + diff --git a/test/test-acl-get-user.js b/test/test-acl-get-user.js new file mode 100644 index 0000000..45abb83 --- /dev/null +++ b/test/test-acl-get-user.js @@ -0,0 +1,180 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +//path.verb.role +var acl = require('./acl-get-user.js'); + +describe('acl get user tests - with bad identity', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); + + }) + + describe('get put and post', function () { + it('should throw a 401', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('should throw a 401', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('should throw a 401', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + }) +}) + +describe('acl get user tests - with empty identity', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = ''; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); + + }) + + describe('get put and post', function () { + it('should throw a 401', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('should throw a 401', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('should throw a 401', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + }) +}) + + + +describe('acl get user tests - with good identity', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); + + }) + + describe('get put and post', function () { + it('shoudl be OK - 200', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + }) + +}); diff --git a/test/test-acl-loopquerystring.js b/test/test-acl-loopquerystring.js new file mode 100644 index 0000000..faf29b4 --- /dev/null +++ b/test/test-acl-loopquerystring.js @@ -0,0 +1,105 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); +//path.verb.role +var acl = require('./acl-get-multipleusers.js'); + +function generateString(length) { + var text = ""; + var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*()-_+=!@~<>?/.,[]}{\|}"; + for (var i = 0; i < length; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + +var app; +var router; +var randomPaths = new Array(1000); + +var testTimeout = 20000; + +for (var i = 0; i < randomPaths.length; i++) { + var randomString = generateString(40); + randomPaths[i] = randomString; +} + +for (var i = 0; i < randomPaths.length; i++) { + var randomString = generateString(40); + randomPaths[i] = randomString; +} + + +function makeTest(path, status) { + it('testing path and should be ' + status + ' for ' + path, function (done) { + request(app) + .post(path) + .send({ message: "stuff" }) + .expect(status) + .end(done); + }); +} + +describe('this is the loop for passing tests', function () { + this.timeout(testTimeout); + before(function () { + + app = express(); + router = express.Router(); + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + //Norml middleware usage.. + router.all('*', lib(acl)); + //mock handlers + var handlers = require('./handlers'); + router.get('/fatfinger', handlers[0]); + router.post('/fatfinger', handlers[1]); + router.put('/fatfinger', handlers[2]); + app.use('/', router); + + }) + + //these tests have a ? for query string. + for (var i = 0; i < randomPaths.length; i++) { + makeTest('/fatfinger?' + randomPaths[i], 200); + } + return; +}) + +describe('this is the loop for failing tests', function () { + this.timeout(testTimeout); + before(function () { + + app = express(); + router = express.Router(); + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + //Norml middleware usage.. + router.all('*', lib(acl)); + //mock handlers + var handlers = require('./handlers'); + router.get('/foo', handlers[0]); + router.post('/foo', handlers[1]); + router.put('/foo', handlers[2]); + app.use('/', router); + + }) + + // this has NO ? for query string + for (var i = 0; i < randomPaths.length; i++) { + makeTest('/foo?' + randomPaths[i], 401); + } + return; +}) diff --git a/test/test-acl-publicall.js b/test/test-acl-publicall.js new file mode 100644 index 0000000..14ba20b --- /dev/null +++ b/test/test-acl-publicall.js @@ -0,0 +1,69 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +//path.verb.role +var acl = require('./acl-get-multipleusers.js'); + +describe('acl public get all; user just get', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/publicall', handlers[0]); + router.post('/publicall', handlers[1]); + router.put('/publicall', handlers[2]); + app.use('/', router); + + }) + + describe('publicall verbs', function () { + it('shoudl be OK - 200', function (done) { + request(app) + .get('/publicall') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .put('/publicall') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .post('/publicall') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + }) + +}) + diff --git a/test/test-acl-querystrings.js b/test/test-acl-querystrings.js new file mode 100644 index 0000000..85a9b59 --- /dev/null +++ b/test/test-acl-querystrings.js @@ -0,0 +1,126 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +//path.verb.role +var acl = require('./acl-get-multipleusers.js'); + + +describe('bunch of query string junk', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/fatfinger', handlers[0]); + router.post('/fatfinger', handlers[1]); + router.put('/fatfinger', handlers[2]); + app.use('/', router); + + }) + + describe('query string junk', function () { + it('shoudl be OK - 200', function (done) { + request(app) + .get('/fatfinger?foobar=nothing#/simplestuff') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .put('/fatfinger?foobar=nothing#/simplestuff') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .post('/fatfinger?foobar=nothing#/simplestuff') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + }) + +}) + +describe('query string junk', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity= 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/fatfinger', handlers[0]); + router.post('/fatfinger', handlers[1]); + router.put('/fatfinger', handlers[2]); + app.use('/', router); + + }) + + describe('query string junk', function () { + it('should be 401', function (done) { + request(app) + .get('/fatfinger?foobar=nothing#/simplestuff') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('should be 401', function (done) { + request(app) + .put('/fatfinger?foobar=nothing#/simplestuff') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('should be 401', function (done) { + request(app) + .post('/fatfinger?foobar=nothing#/simplestuff') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + }) + +}) + diff --git a/test/test-acl-userall.js b/test/test-acl-userall.js new file mode 100644 index 0000000..f815112 --- /dev/null +++ b/test/test-acl-userall.js @@ -0,0 +1,126 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +//path.verb.role +var acl = require('./acl-get-multipleusers.js'); + + +describe('user - all', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/publicget', handlers[0]); + router.post('/publicget', handlers[1]); + router.put('/publicget', handlers[2]); + app.use('/', router); + + }) + + describe('publicget verbs', function () { + it('shoudl be OK - 200', function (done) { + request(app) + .get('/publicget') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .put('/publicget') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('shoudl be OK - 200', function (done) { + request(app) + .post('/publicget') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + }) + +}) + +describe('publicget - public - only get', function () { + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity= 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/publicget', handlers[0]); + router.post('/publicget', handlers[1]); + router.put('/publicget', handlers[2]); + app.use('/', router); + + }) + + describe('publicget - just get', function () { + it('should be 200', function (done) { + request(app) + .get('/publicget') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(200, done); + }) + it('should be 401', function (done) { + request(app) + .put('/publicget') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('should be 401', function (done) { + request(app) + .post('/publicget') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + }) + +}) + From 7c185faa91809dc08d0a037974c916c72fd5df6a Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 11:16:34 -0500 Subject: [PATCH 02/19] update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b3d7d10..94f6408 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "salti", "version": "0.1.1", - "description": "Simple Authentication for Thali IoT", + "description": "Simple Authentication and Authorization for Thali IoT", "main": "lib/index.js", "scripts": { "test": "node --harmony ./node_modules/mocha/bin/mocha", From be1cb516f5430d1e325644efd67a3b9a31660f24 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 11:16:37 -0500 Subject: [PATCH 03/19] 0.1.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 94f6408..688bff9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salti", - "version": "0.1.1", + "version": "0.1.2", "description": "Simple Authentication and Authorization for Thali IoT", "main": "lib/index.js", "scripts": { From 56aa599874675abb6e1b564d8df8bace1d469053 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 12:27:02 -0500 Subject: [PATCH 04/19] addming schema --- lib/acl-example.js | 42 +++++----- lib/acl-schema.json | 37 +++++++++ lib/pouchdb.js | 36 ++++----- test/acl-get-multipleusers.js | 38 ++++----- test/acl-get-user.js | 26 +++--- test/acl-path-types.js | 32 ++++++++ test/test-acl-loopquerystring.js | 5 ++ test/test-acl-pathtypes.js | 135 +++++++++++++++++++++++++++++++ 8 files changed, 282 insertions(+), 69 deletions(-) create mode 100644 lib/acl-schema.json create mode 100644 test/acl-path-types.js create mode 100644 test/test-acl-pathtypes.js diff --git a/lib/acl-example.js b/lib/acl-example.js index ad95540..01a620d 100644 --- a/lib/acl-example.js +++ b/lib/acl-example.js @@ -1,30 +1,34 @@ -'use strict'; +"use strict"; + +//go here with the schema and build away... +//http://jeremydorn.com/json-editor/ +//http://bit.ly/1Qs3l66 module.exports = [{ - path: '/', - roles: [{ - role: 'public', - verbs: ['get'] + "path": "/", + "roles": [{ + "role": "public", + "verbs": ["get"] }, { - role: 'user', - verbs: ['get'] + "role": "user", + "verbs": ["get"] }] }, { - path: '/foo', - roles: [{ - role: 'public', - verbs: ['get', 'post'] + "path": "/foo", + "roles": [{ + "role": "public", + "verbs": ["get", "post"] }, { - role: 'user', - verbs: ['get', 'put', 'post'] + "role": "user", + "verbs": ["get", "put", "post"] }] }, { - path: '/bar', - roles: [{ - role: 'public', - verbs: ['get'] + "path": "/bar", + "roles": [{ + "role": "public", + "verbs": ["get"] }, { - role: 'user', - verbs: ['get'] + "role": "user", + "verbs": ["get"] }] }]; diff --git a/lib/acl-schema.json b/lib/acl-schema.json new file mode 100644 index 0000000..e402caf --- /dev/null +++ b/lib/acl-schema.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "/", + "type": "array", + "items": { + "id": "1", + "type": "object", + "properties": { + "path": { + "id": "path", + "type": "string" + }, + "roles": { + "id": "roles", + "type": "array", + "items": { + "id": "1", + "type": "object", + "properties": { + "role": { + "id": "role", + "type": "string" + }, + "verbs": { + "id": "verbs", + "type": "array", + "items": { + "id": "0", + "type": "string" + } + } + } + } + } + } + } +} diff --git a/lib/pouchdb.js b/lib/pouchdb.js index 3781de6..0ef0990 100644 --- a/lib/pouchdb.js +++ b/lib/pouchdb.js @@ -1,33 +1,33 @@ 'use strict'; module.exports = [{ - path: '/_utils/', - roles: [{ - role: 'public', - verbs: ['get'] + "path": '/_utils/', + "roles": [{ + "role": 'public', + "verbs": ['get'] }, { - role: 'user', - verbs: ['get'] + "role": 'user', + "verbs": ['get'] }] }, { - path: '/db/', - roles: [{ - role: 'public', - verbs: ['get'] + "path": '/db/', + "roles": [{ + "role": 'public', + "verbs": ['get'] }] }, { - path: '/_utils/css/', - roles: [{ - role: 'public', - verbs: ['get'] + "path": '/_utils/css/', + "roles": [{ + "role": 'public', + "verbs": ['get'] }] }, { - path: '/_utils/js/', - roles: [{ - role: 'public', - verbs: ['get'] + "path": '/_utils/js/', + "roles": [{ + "role": 'public', + "verbs": ['get'] }] }]; diff --git a/test/acl-get-multipleusers.js b/test/acl-get-multipleusers.js index 2b6033e..deb7944 100644 --- a/test/acl-get-multipleusers.js +++ b/test/acl-get-multipleusers.js @@ -1,31 +1,31 @@ -'use strict'; +"use strict"; module.exports = [ { - path: '/publicall', - roles: [ - {role: 'public', - verbs: ['get', 'post', 'put']}, - {role: 'user', - verbs: ['get']} + "path": "/publicall", + "roles": [ + {"role": "public", + "verbs": ["get", "post", "put"]}, + {"role": "user", + "verbs": ["get"]} ] }, { - path: '/fatfinger', - roles: [ - {role: 'public', - verbs: []}, - {role: 'user', - verbs: ['post', 'put', 'get', 'get', 'put', 'post']} + "path": "/fatfinger", + "roles": [ + {"role": "public", + "verbs": []}, + {"role": "user", + "verbs": ["post", "put", "get", "get", "put", "post"]} ] }, { - path: '/publicget', - roles: [ - {role: 'public', - verbs: ['get']}, - {role: 'user', - verbs: ['get', 'post', 'put']} + "path": "/publicget", + "roles": [ + {"role": "public", + "verbs": ["get"]}, + {"role": "user", + "verbs": ["get", "post", "put"]} ] } ]; diff --git a/test/acl-get-user.js b/test/acl-get-user.js index f48994e..ffbb0fa 100644 --- a/test/acl-get-user.js +++ b/test/acl-get-user.js @@ -2,24 +2,24 @@ module.exports = [ { - path: '/', - roles: [ - {role: 'user', - verbs: ['get', 'put', 'post']} + "path": '/', + "roles": [ + {"role": 'user', + "verbs": ['get', 'put', 'post']} ] }, { - path: '/foo', - roles: [ - {role: 'user', - verbs: ['get', 'put', 'post']} + "path": '/foo', + "roles": [ + {"role": 'user', + "verbs": ['get', 'put', 'post']} ] }, { - path: '/bar', - roles: [ - {role: 'user', - verbs: ['get']} + "path": '/bar', + "roles": [ + {"role": 'user', + "verbs": ['get']} ] } -]; +]; \ No newline at end of file diff --git a/test/acl-path-types.js b/test/acl-path-types.js new file mode 100644 index 0000000..db113be --- /dev/null +++ b/test/acl-path-types.js @@ -0,0 +1,32 @@ +'use strict'; + +module.exports = [ + { + "path": '/base', + "roles": [ + {"role": 'public', + "verbs": ['get', 'put', 'post']} + ] + }, + { + "path": '/foo/', + "roles": [ + {"role": 'public', + "verbs": ['get', 'put', 'post']} + ] + }, + { + "path": '/bar/foo', + "roles": [ + {"role": 'public', + "verbs": ['get', 'put', 'post']} + ] + }, + { + "path": '/fiz/baz/', + "roles": [ + {"role": 'public', + "verbs": ['get', 'put', 'post']} + ] + } +]; \ No newline at end of file diff --git a/test/test-acl-loopquerystring.js b/test/test-acl-loopquerystring.js index faf29b4..2dc2c7b 100644 --- a/test/test-acl-loopquerystring.js +++ b/test/test-acl-loopquerystring.js @@ -1,5 +1,10 @@ 'use strict'; +if (! process.env.TEST_LOOP ){ + console.log('not testing loops... set env:TEST_LOOP to run'); + return; +} + var request = require('supertest'), express = require('express'), path = require('path'), diff --git a/test/test-acl-pathtypes.js b/test/test-acl-pathtypes.js new file mode 100644 index 0000000..e9f3671 --- /dev/null +++ b/test/test-acl-pathtypes.js @@ -0,0 +1,135 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +var acl = require('./acl-path-types'); + + +describe('checking for items after a base path', function(){ + + var app; + var router; + + before(function () { + app = express(); + router = express.Router(); + + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) + + //Norml middleware usage.. + router.all('*', lib(acl)); + + //mock handlers + var handlers = require('./handlers'); + + router.get('/base', handlers[0]); + router.get('/foo', handlers[0]); + router.get('/bar/foo', handlers[0]); + router.get('/fiz/baz', handlers[0]); + app.use('/', router); + + }) + + describe('if a path ends in a /', function(){ + it('should allow /base through', function(done){ + request(app) + .get('/base') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should allow /base/myfile.js through', function(done){ + request(app) + .get('/base/myfile.js') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should stop /basemyfile.js', function(done){ + request(app) + .get('/basemyfile.js') + .set('Accept', 'application/json') + .expect(401, done) + }) + }) + + describe('if a path doesnt ends in a / - it should assume it', function(){ + it('should allow /foo/ through', function(done){ + request(app) + .get('/foo/') + .set('Accept', 'application/json') + .expect(200, done) + }) + it('should allow /foo/myfile.js through', function(done){ + request(app) + .get('/foo/myfile.js') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should stop /foomyfile.js', function(done){ + request(app) + .get('/foomyfile.js') + .set('Accept', 'application/json') + .expect(401, done) + }) + }) + + describe('if a path ends in a /', function(){ + it('should allow /bar/foo through', function(done){ + request(app) + .get('/bar/foo') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should allow /bar/foo/myfile.js through', function(done){ + request(app) + .get('/bar/foo/myfile.js') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should stop /bar/foomyfile.js', function(done){ + request(app) + .get('/bar/foomyfile.js') + .set('Accept', 'application/json') + .expect(401, done) + }) + }) + + describe('if a path doesnt ends in a / - it should assume it', function(){ + it('should allow /fiz/baz/ through', function(done){ + request(app) + .get('/fiz/baz/') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should allow /fiz/baz/myfile.js through', function(done){ + request(app) + .get('/fiz/baz/myfile.js') + .set('Accept', 'application/json') + .expect(200, done) + }) + + it('should stop /fiz/bazmyfile.js', function(done){ + request(app) + .get('/fiz/bazmyfile.js') + .set('Accept', 'application/json') + .expect(401, done) + }) + }) + + +}) \ No newline at end of file From 3f19860a19a6656209d27943fdbc70407c7d9a9b Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 14:11:16 -0500 Subject: [PATCH 05/19] adding secure-compare --- lib/acl-example.js | 5 ++- lib/index.js | 46 +++++++++++------------ lib/indexOf.js | 76 ++++++++++++++++++++++++++++++++++++++ test/test-acl-pathtypes.js | 4 ++ 4 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 lib/indexOf.js diff --git a/lib/acl-example.js b/lib/acl-example.js index 01a620d..7205b8e 100644 --- a/lib/acl-example.js +++ b/lib/acl-example.js @@ -4,6 +4,9 @@ //http://jeremydorn.com/json-editor/ //http://bit.ly/1Qs3l66 +//ideally - 1 less check if path has a trailing /.. +// ie. /foo/ is faster than /bar + module.exports = [{ "path": "/", "roles": [{ @@ -14,7 +17,7 @@ module.exports = [{ "verbs": ["get"] }] }, { - "path": "/foo", + "path": "/foo/", "roles": [{ "role": "public", "verbs": ["get", "post"] diff --git a/lib/index.js b/lib/index.js index 6b9c2d9..e4bdf7b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,19 +1,17 @@ 'use strict'; var debug = require('debug')('thalisalti:acl'); +var fast = require('./indexOf'); var anonymous = 'public'; -function mapIt(source, key, match) { - return source.map(function (el) { - debug('\t %s', el); - debug(el[key]); - if (el[key]) - return el[key]; - else - return el; - }).indexOf(match); - //return rv; +function mapIt(subject, key, target) { + var itExists = fast.indexOf(subject, target, key, 0); + if (!~itExists) { + return itExists; + } + + return fast.indexOf(subject, target, key, 0); } module.exports = function (acl) { @@ -23,24 +21,26 @@ module.exports = function (acl) { return function (req, res, next) { debug('method: %s url: %s path: %s', req.method, req.url, req.path); - //do a match with a slash on the end... - //if that fails do a match that is lastindex of... - - //using headers for validation of the acl engine let identity = req.connection.pskIdentity || anonymous; - debug('Identity: %s', identity); - + var pathExists = mapIt(acl, 'path', req.path); debug('req.path: %s', req.path); + + //here we fall back to see if (!~pathExists) { - let justPath = req.path.substring(0, req.path.lastIndexOf("/") + 1); - debug('justpath: %s', justPath); - pathExists = mapIt(acl, 'path', justPath); - if (!~pathExists){ - debug('unauthorized1: %s : %s', identity, req.path); - return res.status(401).send(msg401); + let justPath = req.path.substring(0, req.path.lastIndexOf("/")); + + debug('raw path did not exist now till try with trailing slash'); + pathExists = mapIt(acl, 'path', justPath + '/'); + if (!~pathExists) { + debug('now we try with no trailing slash'); + pathExists = mapIt(acl, 'path', justPath); + if (!~pathExists) { + debug('unauthorized1: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } } } @@ -52,7 +52,7 @@ module.exports = function (acl) { } let verbs = acl[pathExists].roles[roleExists].verbs; - let verbExists = mapIt(verbs, 'verb', req.method.toLowerCase()); + let verbExists = mapIt(verbs, null, req.method.toLowerCase()); if (!~verbExists) { debug('unauthorized3: %s : %s', identity, req.path); diff --git a/lib/indexOf.js b/lib/indexOf.js new file mode 100644 index 0000000..985c799 --- /dev/null +++ b/lib/indexOf.js @@ -0,0 +1,76 @@ +/* istanbul ignore next */ +//taken from: https://github.com/codemix/fast.js/tree/master + +// # The MIT License (MIT) +// +// Copyright (c) 2014 codemix.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +'use strict'; + +var secureCompare = require('secure-compare'); + +var secure = true; + +function compareIt(source, target, secure) { + if (secure) { + if (source === target) { + return true; + } + else { + return false; + } + } + else { + return secureCompare(source, target); + } +} + +module.exports.indexOf = function fastIndexOf(subject, target, key, fromIndex) { + var length = subject.length, + i = 0; + + if (typeof fromIndex === 'number') { + i = fromIndex; + if (i < 0) { + i += length; + if (i < 0) { + i = 0; + } + } + } + + if (key) { + for (; i < length; i++) { + if (compareIt(subject[i][key], target, secure)) { + return i; + } + } + } + else { + for (; i < length; i++) { + if (compareIt(subject[i], target, secure)) { + return i; + } + } + } + return -1; +}; + diff --git a/test/test-acl-pathtypes.js b/test/test-acl-pathtypes.js index e9f3671..13add2c 100644 --- a/test/test-acl-pathtypes.js +++ b/test/test-acl-pathtypes.js @@ -33,9 +33,13 @@ describe('checking for items after a base path', function(){ var handlers = require('./handlers'); router.get('/base', handlers[0]); + router.get('/base/myfile.js', handlers[0]); router.get('/foo', handlers[0]); router.get('/bar/foo', handlers[0]); + router.get('/foo/myfile.js', handlers[0]); + router.get('/bar/foo/myfile.js', handlers[0]); router.get('/fiz/baz', handlers[0]); + router.get('/fiz/baz/myfile.js', handlers[0]); app.use('/', router); }) From 12c60c7b49582f2181ed70c85a841368bc56d634 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 16:01:46 -0500 Subject: [PATCH 06/19] added working sample --- lib/fauxton.js | 91 +++++++++++++++++++++++++++++ lib/index.js | 2 + lib/indexOf.js | 2 +- lib/pouchdb.js | 52 +++++++++-------- sample/app.js | 65 +++++++++++++++++++++ sample/app/validate.js | 44 ++++++++++++++ sample/package.json | 14 ++++- sample/public/stylesheets/style.css | 8 +++ sample/routes/index.js | 9 +++ sample/routes/replicate.js | 71 ++++++++++++++++++++++ sample/routes/validate.js | 51 ++++++++++++++++ sample/server.js | 62 +++++++++++++++++++- sample/views/error.ejs | 3 + sample/views/index.ejs | 15 +++++ sample/views/replicate.ejs | 24 ++++++++ sample/views/validate.ejs | 50 ++++++++++++++++ 16 files changed, 534 insertions(+), 29 deletions(-) create mode 100644 lib/fauxton.js create mode 100644 sample/app.js create mode 100644 sample/app/validate.js create mode 100644 sample/public/stylesheets/style.css create mode 100644 sample/routes/index.js create mode 100644 sample/routes/replicate.js create mode 100644 sample/routes/validate.js create mode 100644 sample/views/error.ejs create mode 100644 sample/views/index.ejs create mode 100644 sample/views/replicate.ejs create mode 100644 sample/views/validate.ejs diff --git a/lib/fauxton.js b/lib/fauxton.js new file mode 100644 index 0000000..b71ae53 --- /dev/null +++ b/lib/fauxton.js @@ -0,0 +1,91 @@ +"use strict"; + +module.exports = [{ + "path": "/_utils", + "roles": [{ + "role": "public", + "verbs": ["get"] + }, { + "role": "user", + "verbs": ["get"] + }] +}, + { + "path": "/db", + "roles": [{ + "role": "public", + "verbs": ["get"] + }] + }, + { + "path": "/_utils/css", + "roles": [{ + "role": "public", + "verbs": ["get"] + }] + }, + { + "path": "/_utils/js", + "roles": [{ + "role": "public", + "verbs": ["get"] + }] + }, { + "path": "/_utils/img", + "roles": [{ + "role": "public", + "verbs": ["get"] + }] + }, { + "path": "/_session", + "roles": [{ + "role": "public", + "verbs": ["get"] + }] + }, { + "path": "/_utils/fonts", + "roles": [{ + "role": "public", + "verbs": ["get"] + }] + }, { + "path": "/foobardb", + "roles": [{ + "role": "public", + "verbs": ["get", "put", "post"] + }] + }, { + "path": "/_all_dbs", + "roles": [{ + "role": "public", + "verbs": ["get", "put", "post"] + }] + }, { + "path": "/_utils/js/zeroclipboard", + "roles": [{ + "role": "public", + "verbs": ["get", "put", "post"] + }] + }, + { + "path": "/_uuids", + "roles": [{ + "role": "public", + "verbs": ["get", "put", "post"] + }] + }, + { + "path": "/favicon.ico", + "roles": [{ + "role": "public", + "verbs": ["get", "put", "post"] + }] + }]; + + +///_uuids + +///_all_dbs +// /_utils/js/zeroclipboard/Z +//db_utils +//db/css/ \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index e4bdf7b..0943280 100644 --- a/lib/index.js +++ b/lib/index.js @@ -53,6 +53,8 @@ module.exports = function (acl) { let verbs = acl[pathExists].roles[roleExists].verbs; let verbExists = mapIt(verbs, null, req.method.toLowerCase()); + + debug('path/role: %s %s', pathExists, roleExists); if (!~verbExists) { debug('unauthorized3: %s : %s', identity, req.path); diff --git a/lib/indexOf.js b/lib/indexOf.js index 985c799..98238c9 100644 --- a/lib/indexOf.js +++ b/lib/indexOf.js @@ -27,7 +27,7 @@ var secureCompare = require('secure-compare'); -var secure = true; +var secure = false; function compareIt(source, target, secure) { if (secure) { diff --git a/lib/pouchdb.js b/lib/pouchdb.js index 0ef0990..cdf93c6 100644 --- a/lib/pouchdb.js +++ b/lib/pouchdb.js @@ -1,37 +1,43 @@ -'use strict'; +"use strict"; -module.exports = [{ - "path": '/_utils/', +module.exports = [{ + "path": "/_validate", + "roles": [{ + "role": "public", + "verbs": ["get", "put", "post"] + }] + }, + { + "path": "/foobarrepl", "roles": [{ - "role": 'public', - "verbs": ['get'] - }, { - "role": 'user', - "verbs": ['get'] + "role": "public", + "verbs": ["get", "post", "put"] }] -}, { - "path": '/db/', + }, + { + "path": "/foobarrepl/_local", "roles": [{ - "role": 'public', - "verbs": ['get'] + "role": "public", + "verbs": ["get", "post", "put"] }] -}, -{ - "path": '/_utils/css/', + }, { + "path": "/_session", "roles": [{ - "role": 'public', - "verbs": ['get'] + "role": "public", + "verbs": ["get"] }] -}, -{ - "path": '/_utils/js/', + }, { + "path": "/_all_dbs", "roles": [{ - "role": 'public', - "verbs": ['get'] + "role": "public", + "verbs": ["get", "put", "post"] }] -}]; + }]; +///_uuids +///_all_dbs +// /_utils/js/zeroclipboard/Z //db_utils //db/css/ \ No newline at end of file diff --git a/sample/app.js b/sample/app.js new file mode 100644 index 0000000..2be78c3 --- /dev/null +++ b/sample/app.js @@ -0,0 +1,65 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var routes = require('./routes/index'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'ejs'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, 'public'))); + +app.use('/node_modules', express.static( path.join(__dirname, 'node_modules'))); +app.use('/app', express.static( path.join(__dirname, 'app'))); + +app.use('/', routes); + + +app.use('/replicate', require('./routes/replicate')); +app.use('/validate', require('./routes/validate')); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +module.exports = app; diff --git a/sample/app/validate.js b/sample/app/validate.js new file mode 100644 index 0000000..1f152d2 --- /dev/null +++ b/sample/app/validate.js @@ -0,0 +1,44 @@ +var myApp = angular.module('myApp', []); + +myApp.controller('ValidateController', ['$scope', '$http', function ($scope, $http) { + $scope.greeting = 'Welcome to SALTI!'; + $scope.errors = 'no errors'; + $scope.messages = 'no messages'; + $scope.secret = 'foobar'; + $scope.newdoc = genDoc(); + + $scope.createDoc = function () { + + var body = { 'secret': 'CLEAR ' + $scope.secret, 'doc' : $scope.newdoc}; + + $http({ + method: 'POST', + url: '/validate', + data: body + }).then(function successCallback(response) { + $scope.newdoc = genDoc(); + if (response.data.secret) + $scope.secret = response.data.secret; + else + $scope.messages = JSON.stringify(response); + }, function errorCallback(response) { + $scope.errors = JSON.stringify(response); + }); + } + +}]); + + +function genDoc(){ + return JSON.stringify({ _id: guid(), "type": "foobar" }) +} + +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +} \ No newline at end of file diff --git a/sample/package.json b/sample/package.json index b958415..87f6d1e 100644 --- a/sample/package.json +++ b/sample/package.json @@ -10,9 +10,17 @@ "author": "Shawn Cicoria", "license": "MIT", "dependencies": { - "debug": "^2.2.0", - "express": "^4.13.4", + "angular": "^1.5.0", + "body-parser": "~1.13.2", + "bootstrap": "^3.3.6", + "cookie-parser": "~1.3.5", + "debug": "~2.2.0", + "ejs": "~2.3.3", + "express": "~4.13.1", "express-pouchdb": "^1.0.2", - "pouchdb": "^5.2.1" + "jquery": "^2.2.1", + "morgan": "~1.6.1", + "pouchdb": "^5.2.1", + "serve-favicon": "~2.3.0" } } diff --git a/sample/public/stylesheets/style.css b/sample/public/stylesheets/style.css new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/sample/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/sample/routes/index.js b/sample/routes/index.js new file mode 100644 index 0000000..bc7ebbe --- /dev/null +++ b/sample/routes/index.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +/* GET home page. */ +router.get('/', function(req, res, next) { + res.render('index', { title: 'SALTI Test Express Site' }); +}); + +module.exports = router; diff --git a/sample/routes/replicate.js b/sample/routes/replicate.js new file mode 100644 index 0000000..556b77e --- /dev/null +++ b/sample/routes/replicate.js @@ -0,0 +1,71 @@ +var express = require('express'); +var router = express.Router(); +var debug = require('debug')('thalisalti:replicate'); +var http = require('http'); + +var pouchDBBase = require('pouchdb'); +var PouchDB = pouchDBBase.defaults({ prefix: './db/' }); + +/* GET home page. */ +router.get('/', function (req, res, next) { + res.render('replicate', { title: 'Replicate' }); +}); + +router.post('/', function (req, res, next) { + + debug('posted request'); + var localDB = new PouchDB('foobar') + + var pouchDbOptions = { ajax : { + agentOptions:{ + rejectUnauthorized: false + } + }}; + + var remoteDB = new PouchDB('http://localhost:3001/foobarrepl', pouchDbOptions) + + localDB.replicate.to(remoteDB).on('complete', function () { + debug('done replication'); + }).on('error', function (err) { + debug('error on replication'); + debug(err); + }); + + res.render('replicate', { title: 'Replicate' }); + +}); + + +router.post('/add', function (req, res, next) { + debug('adding a new record...'); + var db = new PouchDB('foobar'); + var doc = { + "_id": guid(), + "name": "Mittens", + "occupation": "kitten", + "age": 3, + "hobbies": [ + "playing with balls of yarn", + "chasing laser pointers", + "lookin' hella cute" + ] + }; + + db.put(doc); + debug('done a new record...'); + + res.render('replicate', { title: 'Replicate' }); +}) + + +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +} + +module.exports = router; diff --git a/sample/routes/validate.js b/sample/routes/validate.js new file mode 100644 index 0000000..117ecd1 --- /dev/null +++ b/sample/routes/validate.js @@ -0,0 +1,51 @@ +var debug = require('debug')('thalisalti:validate'); +var express = require('express'); +var router = express.Router(); + +var PouchDB = require('pouchdb'); +var pbsetup = PouchDB.defaults({ prefix: './db/' }); + +var pouchDbOptions = { ajax : { + agentOptions:{ + rejectUnauthorized: false + } +}}; + + +/* GET home page. */ +router.get('/', function (req, res, next) { + res.render('validate', { title: 'SALTI-Admin Validate Page' }); +}); + +router.post('/', function (req, res, next) { + if (! req.body.doc) { + debug('invalid request on post - missing doc'); + return res.status(400).send ({ error: 'invalid request - missing doc'}); + } + + if (! req.body.secret) { + debug('no secret provided - continuing...'); + } + + var theDoc = JSON.parse (req.body.doc); + + debug('PUT a newdoc: ' + JSON.stringify(theDoc)); + + pouchDbOptions.ajax.headers = { + 'User-Agent': 'request' + } + + var remoteDB = new PouchDB('http://localhost:3001/_validate', pouchDbOptions) + + remoteDB.put(theDoc, function (err, response) { + if (err) { + debug(err); + + return res.status(500).send( { error: err, message: 'failed to put new doc' }); + } + return res.status(200).send( response ); + }); + +}) + +module.exports = router; diff --git a/sample/server.js b/sample/server.js index f26ca4d..639ce91 100644 --- a/sample/server.js +++ b/sample/server.js @@ -2,11 +2,29 @@ 'use strict'; var express = require('express'), + http = require('http'), app = express(), PouchDB = require('pouchdb'), - router = express.Router(); + router = express.Router(), + debug = require('debug')('thalisalti:express'); + + +//this is our sample UI site on port http://localhost:3000 +// the 2 express apps: +var webApp = require('./app'); +var webAppPort = normalizePort(process.env.PORT || '3000'); + +webApp.set('port', webAppPort); +var webServer = http.createServer(webApp); +webServer.listen(webAppPort, function () { + debug('webServer is listening on port %s', webAppPort); +}); +webServer.on('error', onError); + + var pbsetup = PouchDB.defaults({ prefix: './db/' }); +var pouchPort = normalizePort(process.env.PORT2 || '3001'); var opts = { mode: 'minimumForPouchDB', @@ -25,5 +43,45 @@ var pouchApp = require('express-pouchdb')(pbsetup, opts); router.use('/', pouchApp); app.use('/', router); -app.listen(process.env.PORT || 3000); +app.listen(pouchPort); + + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + if (port >= 0) { + // port number + return port; + } + + return false; +} + +function onError(error, parent) { + if (error.syscall !== 'listen') { + throw error; + } + + // var bind = typeof port === 'string' + // ? 'Pipe ' + port + // : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error('port requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error('port is already in use'); + process.exit(1); + break; + default: + throw error; + } +} \ No newline at end of file diff --git a/sample/views/error.ejs b/sample/views/error.ejs new file mode 100644 index 0000000..7cf94ed --- /dev/null +++ b/sample/views/error.ejs @@ -0,0 +1,3 @@ +

<%= message %>

+

<%= error.status %>

+
<%= error.stack %>
diff --git a/sample/views/index.ejs b/sample/views/index.ejs new file mode 100644 index 0000000..c1f6ab9 --- /dev/null +++ b/sample/views/index.ejs @@ -0,0 +1,15 @@ + + + + + <%= title %> + + + + +

<%= title %>

+ + + + + \ No newline at end of file diff --git a/sample/views/replicate.ejs b/sample/views/replicate.ejs new file mode 100644 index 0000000..7af808f --- /dev/null +++ b/sample/views/replicate.ejs @@ -0,0 +1,24 @@ + + + + Replication + + + +

Replication

+ +
+
+
Click the below to start an on-demand replication...
+ +
+
+ +
+
+
Click the button below to create a test document...
+ +
+
+ + \ No newline at end of file diff --git a/sample/views/validate.ejs b/sample/views/validate.ejs new file mode 100644 index 0000000..d059ffd --- /dev/null +++ b/sample/views/validate.ejs @@ -0,0 +1,50 @@ + + + + + + <%= title %> + + + + + + + +
+

<%= title %>

+
+

+ +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+
+ + +
+ +
+ +
+
+ + + + + + + \ No newline at end of file From 54e308a750e7a86c0a634af3a96dc30e15d05e1b Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 16:09:08 -0500 Subject: [PATCH 07/19] working sample --- sample/views/validate.ejs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sample/views/validate.ejs b/sample/views/validate.ejs index d059ffd..effb3d1 100644 --- a/sample/views/validate.ejs +++ b/sample/views/validate.ejs @@ -25,10 +25,6 @@ - -
- -
From ffb2f606beac979ebfe63e58b010464e5f57c807 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 16:09:29 -0500 Subject: [PATCH 08/19] 0.1.3 --- .vscode/tasks.json | 190 --------------------------------------------- LICENSE | 2 +- lib/index.js | 11 ++- lib/indexOf.js | 16 +++- package.json | 2 +- 5 files changed, 26 insertions(+), 195 deletions(-) delete mode 100644 .vscode/tasks.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 1f31215..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,190 +0,0 @@ -// Available variables which can be used inside of strings. -// ${workspaceRoot}: the root folder of the team -// ${file}: the current opened file -// ${fileBasename}: the current opened file's basename -// ${fileDirname}: the current opened file's dirname -// ${fileExtname}: the current opened file's extension -// ${cwd}: the current working directory of the spawned process - -// A task runner that calls the Typescript compiler (tsc) and -// Compiles a HelloWorld.ts program -{ - "version": "0.1.0", - - // The command is tsc. Assumes that tsc has been installed using npm install -g typescript - "command": "tsc", - - // The command is a shell script - "isShellCommand": true, - - // Show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // args is the HelloWorld program to compile. - "args": ["HelloWorld.ts"], - - // use the standard tsc problem matcher to find compile problems - // in the output. - "problemMatcher": "$tsc" -} - -// A task runner that calls the Typescript compiler (tsc) and -// compiles based on a tsconfig.json file that is present in -// the root of the folder open in VSCode -/* -{ - "version": "0.1.0", - - // The command is tsc. Assumes that tsc has been installed using npm install -g typescript - "command": "tsc", - - // The command is a shell script - "isShellCommand": true, - - // Show the output window only if unrecognized errors occur. - "showOutput": "silent", - - // Tell the tsc compiler to use the tsconfig.json from the open folder. - "args": ["-p", "."], - - // use the standard tsc problem matcher to find compile problems - // in the output. - "problemMatcher": "$tsc" -} -*/ - -// A task runner configuration for gulp. Gulp provides a less task -// which compiles less to css. -/* -{ - "version": "0.1.0", - "command": "gulp", - "isShellCommand": true, - "tasks": [ - { - "taskName": "less", - // Make this the default build command. - "isBuildCommand": true, - // Show the output window only if unrecognized errors occur. - "showOutput": "silent", - // Use the standard less compilation problem matcher. - "problemMatcher": "$lessCompile" - } - ] -} -*/ - -// Uncomment the following section to use jake to build a workspace -// cloned from https://github.com/Microsoft/TypeScript.git -/* -{ - "version": "0.1.0", - // Task runner is jake - "command": "jake", - // Need to be executed in shell / cmd - "isShellCommand": true, - "showOutput": "silent", - "tasks": [ - { - // TS build command is local. - "taskName": "local", - // Make this the default build command. - "isBuildCommand": true, - // Show the output window only if unrecognized errors occur. - "showOutput": "silent", - // Use the redefined Typescript output problem matcher. - "problemMatcher": [ - "$tsc" - ] - } - ] -} -*/ - -// Uncomment the section below to use msbuild and generate problems -// for csc, cpp, tsc and vb. The configuration assumes that msbuild -// is available on the path and a solution file exists in the -// workspace folder root. -/* -{ - "version": "0.1.0", - "command": "msbuild", - "args": [ - // Ask msbuild to generate full paths for file names. - "/property:GenerateFullPaths=true" - ], - "taskSelector": "/t:", - "showOutput": "silent", - "tasks": [ - { - "taskName": "build", - // Show the output window only if unrecognized errors occur. - "showOutput": "silent", - // Use the standard MS compiler pattern to detect errors, warnings - // and infos in the output. - "problemMatcher": "$msCompile" - } - ] -} -*/ - -// Uncomment the following section to use msbuild which compiles Typescript -// and less files. -/* -{ - "version": "0.1.0", - "command": "msbuild", - "args": [ - // Ask msbuild to generate full paths for file names. - "/property:GenerateFullPaths=true" - ], - "taskSelector": "/t:", - "showOutput": "silent", - "tasks": [ - { - "taskName": "build", - // Show the output window only if unrecognized errors occur. - "showOutput": "silent", - // Use the standard MS compiler pattern to detect errors, warnings - // and infos in the output. - "problemMatcher": [ - "$msCompile", - "$lessCompile" - ] - } - ] -} -*/ -// A task runner example that defines a problemMatcher inline instead of using -// a predefined one. -/* -{ - "version": "0.1.0", - "command": "tsc", - "isShellCommand": true, - "args": ["HelloWorld.ts"], - "showOutput": "silent", - "problemMatcher": { - // The problem is owned by the typescript language service. Ensure that the problems - // are merged with problems produced by Visual Studio's language service. - "owner": "typescript", - // The file name for reported problems is relative to the current working directory. - "fileLocation": ["relative", "${cwd}"], - // The actual pattern to match problems in the output. - "pattern": { - // The regular expression. Matches HelloWorld.ts(2,10): error TS2339: Property 'logg' does not exist on type 'Console'. - "regexp": "^([^\\s].*)\\((\\d+|\\d+,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$", - // The match group that denotes the file containing the problem. - "file": 1, - // The match group that denotes the problem location. - "location": 2, - // The match group that denotes the problem's severity. Can be omitted. - "severity": 3, - // The match group that denotes the problem code. Can be omitted. - "code": 4, - // The match group that denotes the problem's message. - "message": 5 - } - } -} -*/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index 6b66b96..dc1cf39 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Shawn Cicoria +Copyright (c) 2016 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/index.js b/lib/index.js index 0943280..333a5fa 100644 --- a/lib/index.js +++ b/lib/index.js @@ -14,6 +14,15 @@ function mapIt(subject, key, target) { return fast.indexOf(subject, target, key, 0); } + +/** + * Implements an Express middleware Authentication Filter + * that validates an identity provided as a property on the request.connection object + * agasint ACL represented as a json object. + * @param {Object} acl - the acl represented as an array of ACL's - generally they + * are represented as path.role.verb. + * @returns {Function} returns a handler of (req, res, next) to the middleware pipeline + */ module.exports = function (acl) { //path.role.verb let msg401 = { success: false, message: 'Unauthorized' }; @@ -65,5 +74,3 @@ module.exports = function (acl) { next(); }; }; - - diff --git a/lib/indexOf.js b/lib/indexOf.js index 98238c9..a34b0da 100644 --- a/lib/indexOf.js +++ b/lib/indexOf.js @@ -1,4 +1,6 @@ /* istanbul ignore next */ +'use strict'; + //taken from: https://github.com/codemix/fast.js/tree/master // # The MIT License (MIT) @@ -23,7 +25,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -'use strict'; + +/** + * Custom indexOf implementation from fast.js. + * @module indesOf + */ var secureCompare = require('secure-compare'); @@ -43,6 +49,14 @@ function compareIt(source, target, secure) { } } +/** + * Does an indexOf from a array/object against a target and optional key + * @param {Object} subject - the source array or object. + * @param {string} target - the value to lookup. + * @param {string} key - an optional property value to use if using an object as subject + * @param {Number} fromIndex - where to start in the subject (offset) + * @returns {Number} the index value where found or -1 if not found + */ module.exports.indexOf = function fastIndexOf(subject, target, key, fromIndex) { var length = subject.length, i = 0; diff --git a/package.json b/package.json index 688bff9..b882dbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salti", - "version": "0.1.2", + "version": "0.1.3", "description": "Simple Authentication and Authorization for Thali IoT", "main": "lib/index.js", "scripts": { From f9963abd4ae8443d5091ee62f9c400527cafc195 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 2 Mar 2016 19:19:00 -0500 Subject: [PATCH 09/19] addressing review items --- lib/index.js | 50 ++++++++++++++++++---------------- lib/indexOf.js | 32 ++++------------------ package.json | 3 +- {lib => sample}/acl-example.js | 12 ++++---- {lib => sample}/fauxton.js | 28 +++++++++---------- {lib => sample}/pouchdb.js | 12 ++++---- sample/server.js | 4 +-- test/acl-get-multipleusers.js | 10 +++---- test/acl-get-user.js | 8 +++--- test/acl-path-types.js | 10 +++---- 10 files changed, 76 insertions(+), 93 deletions(-) rename {lib => sample}/acl-example.js (76%) rename {lib => sample}/fauxton.js (74%) rename {lib => sample}/pouchdb.js (73%) diff --git a/lib/index.js b/lib/index.js index 333a5fa..24d2372 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,23 +4,20 @@ var debug = require('debug')('thalisalti:acl'); var fast = require('./indexOf'); var anonymous = 'public'; +const NOTFOUND = -1; -function mapIt(subject, key, target) { - var itExists = fast.indexOf(subject, target, key, 0); - if (!~itExists) { - return itExists; - } - - return fast.indexOf(subject, target, key, 0); -} +/** + * Acl + * @typedef {Object} Salti.Acl + * TODO: + */ /** * Implements an Express middleware Authentication Filter * that validates an identity provided as a property on the request.connection object - * agasint ACL represented as a json object. - * @param {Object} acl - the acl represented as an array of ACL's - generally they - * are represented as path.role.verb. + * against ACL represented as a json object. + * @param {Salti.Acl} acl - acl represented as an array of path.role.verb * @returns {Function} returns a handler of (req, res, next) to the middleware pipeline */ module.exports = function (acl) { @@ -34,38 +31,45 @@ module.exports = function (acl) { let identity = req.connection.pskIdentity || anonymous; debug('Identity: %s', identity); - var pathExists = mapIt(acl, 'path', req.path); + //here we're looking for an EXACT match as is - with full path and any resource on the end... + //ie. /foobar/myresource.js + var pathExists = fast.indexOf(acl, req.path, 'path'); debug('req.path: %s', req.path); - //here we fall back to see - if (!~pathExists) { + if ( NOTFOUND === pathExists) { + debug('raw path did not exist now till try with trailing slash'); + //lets get the path up to the / but NOT with the slash... let justPath = req.path.substring(0, req.path.lastIndexOf("/")); - debug('raw path did not exist now till try with trailing slash'); - pathExists = mapIt(acl, 'path', justPath + '/'); - if (!~pathExists) { + //here we see if it exists up to the last slash (including the slash) + //ie - /foobar/ + pathExists = fast.indexOf(acl, justPath + '/', 'path'); + if ( NOTFOUND === pathExists) { + //here we fall back to see if the path exists up to the last slash (not including) + //ie - /foobar debug('now we try with no trailing slash'); - pathExists = mapIt(acl, 'path', justPath); - if (!~pathExists) { + pathExists = fast.indexOf(acl, justPath, 'path'); + if ( NOTFOUND === pathExists) { debug('unauthorized1: %s : %s', identity, req.path); return res.status(401).send(msg401); } } } + //at this point the request PATH has passed... let roles = acl[pathExists].roles; - let roleExists = mapIt(roles, 'role', identity); - if (!~roleExists) { + let roleExists = fast.indexOf(roles, identity, 'role'); + if ( NOTFOUND === roleExists) { debug('unauthorized2: %s : %s', identity, req.path); return res.status(401).send(msg401); } let verbs = acl[pathExists].roles[roleExists].verbs; - let verbExists = mapIt(verbs, null, req.method.toLowerCase()); + let verbExists = fast.indexOf(verbs, req.method); debug('path/role: %s %s', pathExists, roleExists); - if (!~verbExists) { + if ( NOTFOUND === verbExists) { debug('unauthorized3: %s : %s', identity, req.path); return res.status(401).send(msg401); } diff --git a/lib/indexOf.js b/lib/indexOf.js index a34b0da..b27b31b 100644 --- a/lib/indexOf.js +++ b/lib/indexOf.js @@ -30,23 +30,13 @@ * Custom indexOf implementation from fast.js. * @module indesOf */ - -var secureCompare = require('secure-compare'); - -var secure = false; - -function compareIt(source, target, secure) { - if (secure) { +function compareIt(source, target) { if (source === target) { return true; } else { return false; } - } - else { - return secureCompare(source, target); - } } /** @@ -57,30 +47,20 @@ function compareIt(source, target, secure) { * @param {Number} fromIndex - where to start in the subject (offset) * @returns {Number} the index value where found or -1 if not found */ -module.exports.indexOf = function fastIndexOf(subject, target, key, fromIndex) { - var length = subject.length, - i = 0; - - if (typeof fromIndex === 'number') { - i = fromIndex; - if (i < 0) { - i += length; - if (i < 0) { - i = 0; - } - } - } +module.exports.indexOf = function fastIndexOf(subject, target, key) { + var length = subject.length; + var i = 0; if (key) { for (; i < length; i++) { - if (compareIt(subject[i][key], target, secure)) { + if (compareIt(subject[i][key], target)) { return i; } } } else { for (; i < length; i++) { - if (compareIt(subject[i], target, secure)) { + if (compareIt(subject[i], target)) { return i; } } diff --git a/package.json b/package.json index b882dbb..0ec73fb 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ }, "homepage": "https://github.com/thaliproject/salti#readme", "dependencies": { - "debug": "^2.2.0", - "secure-compare": "^3.0.1" + "debug": "^2.2.0" }, "devDependencies": { "colors": "^1.1.2", diff --git a/lib/acl-example.js b/sample/acl-example.js similarity index 76% rename from lib/acl-example.js rename to sample/acl-example.js index 7205b8e..071987d 100644 --- a/lib/acl-example.js +++ b/sample/acl-example.js @@ -11,27 +11,27 @@ module.exports = [{ "path": "/", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }, { "role": "user", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/foo/", "roles": [{ "role": "public", - "verbs": ["get", "post"] + "verbs": ["GET", "post"] }, { "role": "user", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "put", "post"] }] }, { "path": "/bar", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }, { "role": "user", - "verbs": ["get"] + "verbs": ["GET"] }] }]; diff --git a/lib/fauxton.js b/sample/fauxton.js similarity index 74% rename from lib/fauxton.js rename to sample/fauxton.js index b71ae53..6585b5b 100644 --- a/lib/fauxton.js +++ b/sample/fauxton.js @@ -4,81 +4,81 @@ module.exports = [{ "path": "/_utils", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }, { "role": "user", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/db", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/_utils/css", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/_utils/js", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/_utils/img", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/_session", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/_utils/fonts", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/foobardb", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }, { "path": "/_all_dbs", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }, { "path": "/_utils/js/zeroclipboard", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }, { "path": "/_uuids", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }, { "path": "/favicon.ico", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }]; @@ -88,4 +88,4 @@ module.exports = [{ ///_all_dbs // /_utils/js/zeroclipboard/Z //db_utils -//db/css/ \ No newline at end of file +//db/css/ diff --git a/lib/pouchdb.js b/sample/pouchdb.js similarity index 73% rename from lib/pouchdb.js rename to sample/pouchdb.js index cdf93c6..98dc9eb 100644 --- a/lib/pouchdb.js +++ b/sample/pouchdb.js @@ -4,33 +4,33 @@ module.exports = [{ "path": "/_validate", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }, { "path": "/foobarrepl", "roles": [{ "role": "public", - "verbs": ["get", "post", "put"] + "verbs": ["GET", "POST", "PUT"] }] }, { "path": "/foobarrepl/_local", "roles": [{ "role": "public", - "verbs": ["get", "post", "put"] + "verbs": ["GET", "POST", "PUT"] }] }, { "path": "/_session", "roles": [{ "role": "public", - "verbs": ["get"] + "verbs": ["GET"] }] }, { "path": "/_all_dbs", "roles": [{ "role": "public", - "verbs": ["get", "put", "post"] + "verbs": ["GET", "PUT", "POST"] }] }]; @@ -40,4 +40,4 @@ module.exports = [{ ///_all_dbs // /_utils/js/zeroclipboard/Z //db_utils -//db/css/ \ No newline at end of file +//db/css/ diff --git a/sample/server.js b/sample/server.js index 639ce91..de5ffef 100644 --- a/sample/server.js +++ b/sample/server.js @@ -34,7 +34,7 @@ var opts = { } var acllib = require('../lib/index'); -var acl = require('../lib/pouchdb'); +var acl = require('./pouchdb'); //Norml middleware usage.. router.all('*', acllib(acl)); @@ -84,4 +84,4 @@ function onError(error, parent) { default: throw error; } -} \ No newline at end of file +} diff --git a/test/acl-get-multipleusers.js b/test/acl-get-multipleusers.js index deb7944..709e73d 100644 --- a/test/acl-get-multipleusers.js +++ b/test/acl-get-multipleusers.js @@ -5,9 +5,9 @@ module.exports = [ "path": "/publicall", "roles": [ {"role": "public", - "verbs": ["get", "post", "put"]}, + "verbs": ["GET", "POST", "PUT"]}, {"role": "user", - "verbs": ["get"]} + "verbs": ["GET"]} ] }, { @@ -16,16 +16,16 @@ module.exports = [ {"role": "public", "verbs": []}, {"role": "user", - "verbs": ["post", "put", "get", "get", "put", "post"]} + "verbs": ["POST", "PUT", "GET", "GET", "PUT", "POST"]} ] }, { "path": "/publicget", "roles": [ {"role": "public", - "verbs": ["get"]}, + "verbs": ["GET"]}, {"role": "user", - "verbs": ["get", "post", "put"]} + "verbs": ["GET", "POST", "PUT"]} ] } ]; diff --git a/test/acl-get-user.js b/test/acl-get-user.js index ffbb0fa..441780d 100644 --- a/test/acl-get-user.js +++ b/test/acl-get-user.js @@ -5,21 +5,21 @@ module.exports = [ "path": '/', "roles": [ {"role": 'user', - "verbs": ['get', 'put', 'post']} + "verbs": ['GET', 'PUT', 'POST']} ] }, { "path": '/foo', "roles": [ {"role": 'user', - "verbs": ['get', 'put', 'post']} + "verbs": ['GET', 'PUT', 'POST']} ] }, { "path": '/bar', "roles": [ {"role": 'user', - "verbs": ['get']} + "verbs": ['GET']} ] } -]; \ No newline at end of file +]; diff --git a/test/acl-path-types.js b/test/acl-path-types.js index db113be..b8a8fef 100644 --- a/test/acl-path-types.js +++ b/test/acl-path-types.js @@ -5,28 +5,28 @@ module.exports = [ "path": '/base', "roles": [ {"role": 'public', - "verbs": ['get', 'put', 'post']} + "verbs": ['GET', 'PUT', 'POST']} ] }, { "path": '/foo/', "roles": [ {"role": 'public', - "verbs": ['get', 'put', 'post']} + "verbs": ['GET', 'PUT', 'POST']} ] }, { "path": '/bar/foo', "roles": [ {"role": 'public', - "verbs": ['get', 'put', 'post']} + "verbs": ['GET', 'PUT', 'POST']} ] }, { "path": '/fiz/baz/', "roles": [ {"role": 'public', - "verbs": ['get', 'put', 'post']} + "verbs": ['GET', 'PUT', 'POST']} ] } -]; \ No newline at end of file +]; From 79cb29546c84268ee1078cfb3443596fa3e56771 Mon Sep 17 00:00:00 2001 From: cicorias Date: Thu, 3 Mar 2016 10:10:34 -0500 Subject: [PATCH 10/19] 0.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ec73fb..f868e49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salti", - "version": "0.1.3", + "version": "0.1.4", "description": "Simple Authentication and Authorization for Thali IoT", "main": "lib/index.js", "scripts": { From a338d059f87825767bc7c4adfee8b881f4a04d71 Mon Sep 17 00:00:00 2001 From: cicorias Date: Thu, 3 Mar 2016 11:05:25 -0500 Subject: [PATCH 11/19] adding readme --- README.md | 211 ++++++++++++++++++++++++++++++++++++++++++ sample/acl-example.js | 4 +- sample/server.js | 2 - 3 files changed, 213 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1f6136d..08d9e56 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,213 @@ # SALTI [![Build Status](https://travis-ci.org/cicorias/salt2.svg?branch=master)](https://travis-ci.org/cicorias/salt2) +[![Coverage Status](https://coveralls.io/repos/github/thaliproject/salti/badge.svg?branch=initial)](https://coveralls.io/github/thaliproject/salti?branch=initial) +[![Coverage Status](https://coveralls.io/repos/github/thaliproject/salti/badge.svg?branch=master)](https://coveralls.io/github/thaliproject/salti?branch=master) + +## Simple Authentication and Authorization Library for Thali IoT + +A very simple `PATH` and `VERB` base filtering of requests. Since PouchDB and Express-PouchDB are REST oriented, their permission model is basicaly REST - ie. `PUT, POST, etc.` +This library is intended to be utilized with Pre-Shared Key (PSK) support. See [JxCore #PR813](https://github.com/jxcore/jxcore/pull/813) + +In addition, this is a simple Express middleware library that gets added to the Express app. +For our needs, it is being added to the Express-Pouch layer and provides simple authentication and authorization through a simplified JSON structure. +## Setup and Configuration +By default, the module, if it doesn't find any `pskIdentity` on the `req.connection` object it will assume its basicaly anonymous and assign sometning called `public'. Thus any ACL rules that have `public` will apply to that. + +For example (see more about the ACL format below) - public is show here - giving `public` access to the path `/foo` for verbs of `GET` and `POST`. + +```json +{ + "path": "/foo/", + "roles": [{ + "role": "public", + "verbs": ["GET", "POST"] + }, { + "role": "user", + "verbs": ["GET", "PUT", "POST"] + }] +} + +``` +### PSK Identity + +When running JxCore using the PSK support, on the server side, it's necessary to add a property to the `reqquest.connection` object - like so: + +```javascript +var server = tls.createServer(serverOptions, function (connection) { + //the object provided is a 'connection' + console.log('%s connected', connection.pskIdentity); + //do something with this - implementation specific... + serverResults.push(connection.pskIdentity + ' ' + (connection.authorized ? 'authorized' : 'not authorized')); + + if (connection.authorized){ + //here we lookup a 'role' which will match what's in the ACL json - + var roleLookup = getRole(connection.pskIdentity); + connection.pskIdentity = roleLookup; + }; + //blah + connection.once('data', function (data) { + // + }); + +}); + +``` + +## Configuration of the Express-PouchDB server + +### Run the npm install + +This is an npm module and can be installed as follows: + +```bash +npm install --save salti + +``` + +This sets it up for use in a NodeJS app. + +### Putting the module in the Express app. + +```javascript + +var express = require('express'), + http = require('http'), + PouchDB = require('pouchdb'), + router = express.Router(); + +//this tells PouchDB to use a subdirectory in the root. +var pbsetup = PouchDB.defaults({ prefix: './db/' }); +var pouchPort = normalizePort(process.env.PORT2 || '3001'); + +//here we're using the express-pouchdb configuration for a minimal server. +var opts = { + mode: 'minimumForPouchDB' +} + +//this load the library.. +var acllib = require('salti'); + +//this loads the JSON - which is an example - you're JSON can come from anywhere but should match the json schema +var acl = require('./pouchdb'); + +//Norml middleware usage - this adds it to the ruter... +router.all('*', acllib(acl)); + +//this is the express-pouchdb app - +var pouchApp = require('express-pouchdb')(pbsetup, opts); + +router.use('/', pouchApp); +app.use('/', router); + +app.listen(pouchPort); + +``` + +## JSON Schema + +The ACL file follows this schema. You can use the site [http://jeremydorn.com/json-editor/](http://jeremydorn.com/json-editor/) to manage JSON based upon any schema. + +Or, use this url to manage something based upon THIS schema. [http://bit.ly/1Qs3l66](http://bit.ly/1Qs3l66). + +```json +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "id": "/", + "type": "array", + "items": { + "id": "1", + "type": "object", + "properties": { + "path": { + "id": "path", + "type": "string" + }, + "roles": { + "id": "roles", + "type": "array", + "items": { + "id": "1", + "type": "object", + "properties": { + "role": { + "id": "role", + "type": "string" + }, + "verbs": { + "id": "verbs", + "type": "array", + "items": { + "id": "0", + "type": "string" + } + } + } + } + } + } + } +} + +``` + +### Example ACL Json + +The following is an example of the JSON used for the ACL. + +Note that it is a "module" that returns an Array of Objects. + +#### Example single ACL + +Each Object has a `path`, then a `role` that is a Array of `role.verbs`. + +For example, below we have a single `path` that 'public' can issue `GET', and `user` can issue `GET` and `POST`. + +```json + "path": "/foobar", + "roles": [{ + "role": "public", + "verbs": ["GET"] + }, { + "role": "user", + "verbs": ["GET", "POST"] + }] +} + +``` +#### Exmaple Module used in sample. + +```javascript + +"use strict"; + + +module.exports = [{ + "path": "/foobar", + "roles": [{ + "role": "public", + "verbs": ["GET"] + }, { + "role": "user", + "verbs": ["GET"] + }] +}, { + "path": "/foo/", + "roles": [{ + "role": "public", + "verbs": ["GET", "POST"] + }, { + "role": "user", + "verbs": ["GET", "PUR", "POST"] + }] +}, { + "path": "/bar", + "roles": [{ + "role": "public", + "verbs": ["GET"] + }, { + "role": "user", + "verbs": ["GET"] + }] +}]; +``` + diff --git a/sample/acl-example.js b/sample/acl-example.js index 071987d..f413e87 100644 --- a/sample/acl-example.js +++ b/sample/acl-example.js @@ -20,10 +20,10 @@ module.exports = [{ "path": "/foo/", "roles": [{ "role": "public", - "verbs": ["GET", "post"] + "verbs": ["GET", "POST"] }, { "role": "user", - "verbs": ["GET", "put", "post"] + "verbs": ["GET", "PUR", "POST"] }] }, { "path": "/bar", diff --git a/sample/server.js b/sample/server.js index de5ffef..785c753 100644 --- a/sample/server.js +++ b/sample/server.js @@ -21,8 +21,6 @@ webServer.listen(webAppPort, function () { }); webServer.on('error', onError); - - var pbsetup = PouchDB.defaults({ prefix: './db/' }); var pouchPort = normalizePort(process.env.PORT2 || '3001'); From b8e90fa3cd25d543e1236d084e2946d84264ae19 Mon Sep 17 00:00:00 2001 From: cicorias Date: Thu, 3 Mar 2016 11:05:29 -0500 Subject: [PATCH 12/19] Further Code Review Items --- .travis.yml | 1 + lib/index.js | 58 +++---- newtests.txt | 29 ++++ package.json | 2 +- test/acl-path-types.js | 4 +- test/test-acl-allfail-nopath.js | 270 ++++++++++++++++--------------- test/test-acl-fatfinger.js | 2 +- test/test-acl-get-user.js | 4 +- test/test-acl-loopquerystring.js | 2 +- test/test-acl-pathtypes.js | 93 ++++++----- test/test-acl-publicall.js | 4 +- test/test-acl-querystrings.js | 4 +- test/test-acl-userall.js | 4 +- 13 files changed, 259 insertions(+), 218 deletions(-) create mode 100644 newtests.txt diff --git a/.travis.yml b/.travis.yml index de22f9b..ba01740 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ node_js: - "4.1" - "0.12" - "0.11" + - "0.10" os: - linux script: "npm run-script test-travis" diff --git a/lib/index.js b/lib/index.js index 24d2372..b2f5738 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,7 +4,7 @@ var debug = require('debug')('thalisalti:acl'); var fast = require('./indexOf'); var anonymous = 'public'; -const NOTFOUND = -1; +var NOTFOUND = -1; /** * Acl @@ -22,54 +22,46 @@ const NOTFOUND = -1; */ module.exports = function (acl) { //path.role.verb - let msg401 = { success: false, message: 'Unauthorized' }; + var msg401 = { success: false, message: 'Unauthorized' }; return function (req, res, next) { debug('method: %s url: %s path: %s', req.method, req.url, req.path); //using headers for validation of the acl engine - let identity = req.connection.pskIdentity || anonymous; + var identity = req.connection.pskIdentity || anonymous; debug('Identity: %s', identity); - //here we're looking for an EXACT match as is - with full path and any resource on the end... - //ie. /foobar/myresource.js - var pathExists = fast.indexOf(acl, req.path, 'path'); - debug('req.path: %s', req.path); - - if ( NOTFOUND === pathExists) { - debug('raw path did not exist now till try with trailing slash'); - //lets get the path up to the / but NOT with the slash... - let justPath = req.path.substring(0, req.path.lastIndexOf("/")); + //if the path is just '/' then use that, otherwise, find the path + //without the trailing '/' + var path = req.path === '/' ? '/' + : req.path.slice(-1) === '/' ? req.path.slice(0, -1) + : req.path; + + var pathExists = fast.indexOf(acl, path, 'path'); - //here we see if it exists up to the last slash (including the slash) - //ie - /foobar/ - pathExists = fast.indexOf(acl, justPath + '/', 'path'); - if ( NOTFOUND === pathExists) { - //here we fall back to see if the path exists up to the last slash (not including) - //ie - /foobar - debug('now we try with no trailing slash'); - pathExists = fast.indexOf(acl, justPath, 'path'); - if ( NOTFOUND === pathExists) { - debug('unauthorized1: %s : %s', identity, req.path); - return res.status(401).send(msg401); - } - } + //here we're looking for an EXACT match as is - with full path and NO resource on the end... + //ie. /foobar/myresource.js - this will fail if /foobar is in the ACL's + + if (NOTFOUND === pathExists) { + debug('unauthorized1: %s : %s', identity, req.path); + return res.status(401).send(msg401); } - //at this point the request PATH has passed... - let roles = acl[pathExists].roles; - let roleExists = fast.indexOf(roles, identity, 'role'); - if ( NOTFOUND === roleExists) { + //roles are an array of objects with properyt of 'role' + var roles = acl[pathExists].roles; + var roleExists = fast.indexOf(roles, identity, 'role'); + if (NOTFOUND === roleExists) { debug('unauthorized2: %s : %s', identity, req.path); return res.status(401).send(msg401); } - let verbs = acl[pathExists].roles[roleExists].verbs; - let verbExists = fast.indexOf(verbs, req.method); - + //verbs are just an array of strings + var verbs = roles[roleExists].verbs; + var verbExists = fast.indexOf(verbs, req.method); + debug('path/role: %s %s', pathExists, roleExists); - if ( NOTFOUND === verbExists) { + if (NOTFOUND === verbExists) { debug('unauthorized3: %s : %s', identity, req.path); return res.status(401).send(msg401); } diff --git a/newtests.txt b/newtests.txt new file mode 100644 index 0000000..a98cc71 --- /dev/null +++ b/newtests.txt @@ -0,0 +1,29 @@ + + * | Path | Method | PouchDB API | + * |------|--------|-------------| + * | / | GET | NA [3] | + * | /:db | GET | info | + * | /:db/_all_docs | GET, HEAD, POST [1] | allDocs | + * | /:db/_bulk_get | POST | bulkGet | + * | /:db/_changes | GET, POST [2] | changes | + * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | + * | /:db/_local/:id | + * | /:db/_revs_diff | POST | revsDiff | + * | /:db/:id | GET | get | + + review the detailed spec + https://github.com/thaliproject/Thali_CordovaPlugin/blob/vNext/thali/NextGeneration/security/thaliACLLayer.js + + + + if no identity, verify that it's loopback address calling. + + * If req.connection.pskIdentity is null/undefined then we MUST: + * - check req.ip to make sure that the IP address is set to + * "127.0.0.1" + * - make a case sensitive string compare and make sure that + * the value of the authorization header after the "CLEAR" keyword matches + * the value set in this object. + * If both checks pass then we MUST call next(). Otherwise we MUST immediately + * return a 401 Unauthorized and close the connection as defined below. For now + * we won't worry about also returning a www-authenticate header. \ No newline at end of file diff --git a/package.json b/package.json index f868e49..ecf5d83 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salti", - "version": "0.1.4", + "version": "0.1.5", "description": "Simple Authentication and Authorization for Thali IoT", "main": "lib/index.js", "scripts": { diff --git a/test/acl-path-types.js b/test/acl-path-types.js index b8a8fef..fdfa288 100644 --- a/test/acl-path-types.js +++ b/test/acl-path-types.js @@ -9,7 +9,7 @@ module.exports = [ ] }, { - "path": '/foo/', + "path": '/foo', "roles": [ {"role": 'public', "verbs": ['GET', 'PUT', 'POST']} @@ -23,7 +23,7 @@ module.exports = [ ] }, { - "path": '/fiz/baz/', + "path": '/fiz/baz', "roles": [ {"role": 'public', "verbs": ['GET', 'PUT', 'POST']} diff --git a/test/test-acl-allfail-nopath.js b/test/test-acl-allfail-nopath.js index 81b9086..69e0a4b 100644 --- a/test/test-acl-allfail-nopath.js +++ b/test/test-acl-allfail-nopath.js @@ -11,170 +11,174 @@ var lib = require(path.join(__dirname, '../lib/index')); //path.verb.role var acl = require('./acl-get-multipleusers.js'); -describe('no paths OK all should fail', function () { +describe('acl-allfail-nopath', function () { + describe('no paths OK all should fail', function () { - var app; - var router; + var app; + var router; - before(function () { - app = express(); - router = express.Router(); + before(function () { + app = express(); + router = express.Router(); - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity= 'public'; - next(); - }) + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); + //Norml middleware usage.. + router.all('*', lib(acl)); - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); + //mock handlers + var handlers = require('./handlers'); - }) + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); - describe('should throw a 401', function () { - it('when no path exists', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); }) - it('when no path exists', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); + + describe('should throw a 401', function () { + it('when no path exists', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) }) }) -}) -describe('acl get user tests - with empty identity', function () { + describe('acl get user tests - with empty identity', function () { - var app; - var router; + var app; + var router; - before(function () { - app = express(); - router = express.Router(); + before(function () { + app = express(); + router = express.Router(); - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = ''; - next(); - }) + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = ''; + next(); + }) - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); + //Norml middleware usage.. + router.all('*', lib(acl)); - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); + //mock handlers + var handlers = require('./handlers'); - }) + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); - describe('should throw a 401', function () { - it('when no path exists', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); }) - it('when no path exists', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); + + describe('should throw a 401', function () { + it('when no path exists', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'public') + .expect(401, done); + }) }) }) -}) -describe('acl get user tests - with good identity', function () { + describe('acl get user tests - with good identity', function () { - var app; - var router; + var app; + var router; - before(function () { - app = express(); - router = express.Router(); + before(function () { + app = express(); + router = express.Router(); - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) + //mocker.. + router.all('*', function (req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) - //Norml middleware usage.. - router.all('*', lib(acl)); + //Norml middleware usage.. + router.all('*', lib(acl)); - //mock handlers - var handlers = require('./handlers'); + //mock handlers + var handlers = require('./handlers'); - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); + router.get('/', handlers[0]); + router.post('/', handlers[1]); + router.put('/', handlers[2]); + app.use('/', router); - }) - - describe('should throw a 401', function () { - it('when no path exists', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); }) - it('when no path exists', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); + + describe('should throw a 401', function () { + it('when no path exists', function (done) { + request(app) + .get('/') + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .put('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) + it('when no path exists', function (done) { + request(app) + .post('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + // .set('Identity', 'user') + .expect(401, done); + }) }) - }) -}); + }); + + +}) \ No newline at end of file diff --git a/test/test-acl-fatfinger.js b/test/test-acl-fatfinger.js index 483443a..02cffec 100644 --- a/test/test-acl-fatfinger.js +++ b/test/test-acl-fatfinger.js @@ -40,7 +40,7 @@ describe('fatfinger - user - mixed and duplicate', function () { }) - describe('fatfinger verbs', function () { + describe('fatfinger verbs to /fatfinger', function () { it('shoudl be OK - 200', function (done) { request(app) .get('/fatfinger') diff --git a/test/test-acl-get-user.js b/test/test-acl-get-user.js index 45abb83..4253eda 100644 --- a/test/test-acl-get-user.js +++ b/test/test-acl-get-user.js @@ -11,7 +11,7 @@ var lib = require(path.join(__dirname, '../lib/index')); //path.verb.role var acl = require('./acl-get-user.js'); -describe('acl get user tests - with bad identity', function () { +describe('acl-get-user acl get user tests - with bad identity', function () { var app; var router; @@ -151,7 +151,7 @@ describe('acl get user tests - with good identity', function () { }) - describe('get put and post', function () { + describe('get put and post to a root path /', function () { it('shoudl be OK - 200', function (done) { request(app) .get('/') diff --git a/test/test-acl-loopquerystring.js b/test/test-acl-loopquerystring.js index 2dc2c7b..49890f3 100644 --- a/test/test-acl-loopquerystring.js +++ b/test/test-acl-loopquerystring.js @@ -51,7 +51,7 @@ function makeTest(path, status) { }); } -describe('this is the loop for passing tests', function () { +describe('acl-loopquerystrings - this is the loop for passing tests', function () { this.timeout(testTimeout); before(function () { diff --git a/test/test-acl-pathtypes.js b/test/test-acl-pathtypes.js index 13add2c..282031d 100644 --- a/test/test-acl-pathtypes.js +++ b/test/test-acl-pathtypes.js @@ -11,8 +11,8 @@ var lib = require(path.join(__dirname, '../lib/index')); var acl = require('./acl-path-types'); -describe('checking for items after a base path', function(){ - +describe('acl-pathtypes - checking for items after a base path', function () { + var app; var router; @@ -43,96 +43,111 @@ describe('checking for items after a base path', function(){ app.use('/', router); }) - - describe('if a path ends in a /', function(){ - it('should allow /base through', function(done){ + + describe('if a path ends in a /', function () { + it('should allow /base through', function (done) { request(app) .get('/base') .set('Accept', 'application/json') - .expect(200, done) + .expect(200, done) }) - - it('should allow /base/myfile.js through', function(done){ + + it('should NOT /base/myfile.js through', function (done) { request(app) .get('/base/myfile.js') .set('Accept', 'application/json') - .expect(200, done) + .expect(401, done) }) - - it('should stop /basemyfile.js', function(done){ + + it('should stop /basemyfile.js', function (done) { request(app) .get('/basemyfile.js') .set('Accept', 'application/json') - .expect(401, done) + .expect(401, done) }) }) - describe('if a path doesnt ends in a / - it should assume it', function(){ - it('should allow /foo/ through', function(done){ + describe('if a path doesnt ends in a / - it should assume it', function () { + it('should allow /foo/ through', function (done) { request(app) .get('/foo/') .set('Accept', 'application/json') - .expect(200, done) - }) - it('should allow /foo/myfile.js through', function(done){ + .expect(200, done) + }) + it('should NOT allow /foo/myfile.js through', function (done) { request(app) .get('/foo/myfile.js') .set('Accept', 'application/json') - .expect(200, done) + .expect(401, done) }) - - it('should stop /foomyfile.js', function(done){ + + it('should stop /foomyfile.js', function (done) { request(app) .get('/foomyfile.js') .set('Accept', 'application/json') - .expect(401, done) + .expect(401, done) }) }) - describe('if a path ends in a /', function(){ - it('should allow /bar/foo through', function(done){ + describe('if a path ends in a /', function () { + it('should allow /bar/foo through', function (done) { request(app) .get('/bar/foo') .set('Accept', 'application/json') - .expect(200, done) + .expect(200, done) }) - - it('should allow /bar/foo/myfile.js through', function(done){ + + it('should NOT allow /bar/foo/myfile.js through', function (done) { request(app) .get('/bar/foo/myfile.js') .set('Accept', 'application/json') - .expect(200, done) + .expect(401, done) }) - - it('should stop /bar/foomyfile.js', function(done){ + + it('should stop /bar/foomyfile.js', function (done) { request(app) .get('/bar/foomyfile.js') .set('Accept', 'application/json') - .expect(401, done) + .expect(401, done) }) }) - describe('if a path doesnt ends in a / - it should assume it', function(){ - it('should allow /fiz/baz/ through', function(done){ + describe('if a path doesnt ends in a / - it should assume it', function () { + it('should allow /fiz/baz/ through', function (done) { request(app) .get('/fiz/baz/') .set('Accept', 'application/json') - .expect(200, done) + .expect(200, done) }) - - it('should allow /fiz/baz/myfile.js through', function(done){ + + it('should NOT allow /fiz/baz/myfile.js through', function (done) { request(app) .get('/fiz/baz/myfile.js') .set('Accept', 'application/json') - .expect(200, done) + .expect(401, done) }) - - it('should stop /fiz/bazmyfile.js', function(done){ + + it('should stop /fiz/bazmyfile.js', function (done) { request(app) .get('/fiz/bazmyfile.js') .set('Accept', 'application/json') - .expect(401, done) + .expect(401, done) + }) + }) + describe('few munged paths using / - ', function () { + it('should NOT allow / through', function (done) { + request(app) + .get('/ ') + .set('Accept', 'application/json') + .expect(401, done) + }) + it('should NOT allow /# through', function (done) { + request(app) + .get('/#') + .set('Accept', 'application/json') + .expect(401, done) }) + }) diff --git a/test/test-acl-publicall.js b/test/test-acl-publicall.js index 14ba20b..51f02e6 100644 --- a/test/test-acl-publicall.js +++ b/test/test-acl-publicall.js @@ -11,7 +11,7 @@ var lib = require(path.join(__dirname, '../lib/index')); //path.verb.role var acl = require('./acl-get-multipleusers.js'); -describe('acl public get all; user just get', function () { +describe('acl-publicall - acl public get all; user just get', function () { var app; var router; @@ -39,7 +39,7 @@ describe('acl public get all; user just get', function () { }) - describe('publicall verbs', function () { + describe('publicall verbs to /publicall', function () { it('shoudl be OK - 200', function (done) { request(app) .get('/publicall') diff --git a/test/test-acl-querystrings.js b/test/test-acl-querystrings.js index 85a9b59..78a77cb 100644 --- a/test/test-acl-querystrings.js +++ b/test/test-acl-querystrings.js @@ -12,7 +12,7 @@ var lib = require(path.join(__dirname, '../lib/index')); var acl = require('./acl-get-multipleusers.js'); -describe('bunch of query string junk', function () { +describe('acl-querystrings - bunch of query string junk', function () { var app; var router; @@ -40,7 +40,7 @@ describe('bunch of query string junk', function () { }) - describe('query string junk', function () { + describe('query string junk to /fatfinger?xxxxx#dkdkdkd', function () { it('shoudl be OK - 200', function (done) { request(app) .get('/fatfinger?foobar=nothing#/simplestuff') diff --git a/test/test-acl-userall.js b/test/test-acl-userall.js index f815112..c7722d2 100644 --- a/test/test-acl-userall.js +++ b/test/test-acl-userall.js @@ -12,7 +12,7 @@ var lib = require(path.join(__dirname, '../lib/index')); var acl = require('./acl-get-multipleusers.js'); -describe('user - all', function () { +describe('acl-userall - user - all', function () { var app; var router; @@ -40,7 +40,7 @@ describe('user - all', function () { }) - describe('publicget verbs', function () { + describe('publicget verbs to /publicget...', function () { it('shoudl be OK - 200', function (done) { request(app) .get('/publicget') From f008fa5f7dafc1be11a4f518b5c4db23390653bc Mon Sep 17 00:00:00 2001 From: cicorias Date: Mon, 7 Mar 2016 09:16:49 -0500 Subject: [PATCH 13/19] adding acceptance test --- test/test-acceptance.js | 133 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 test/test-acceptance.js diff --git a/test/test-acceptance.js b/test/test-acceptance.js new file mode 100644 index 0000000..e7434f2 --- /dev/null +++ b/test/test-acceptance.js @@ -0,0 +1,133 @@ + +/** + * we define 2 distinct 'roles' (other than Admin) + * 1. Pull Replication + * 2. Beacon - using a well-known PSK identity of beacon + * All other requests are non-authenticated and purely anonymous are rejected. + */ + +describe('for all roles and users - if the request is just a /', function() { + it('should be 401 - unauthorized', function(done) { + + done(); + }) +}) + +describe('when the user is pskIdentity = beacon', function() { + describe('and he requests any path other than the "beacon" path', function() { + //todo: what is the beacon path + it('POST/GET/PUT should fail with a 401', function() { + + }) + it('IF a GET should be OK 200', function() { + + }) + }) + + describe('if the user is in the Anymous role', function() { + /** + * Anonymous Role - NO CLEAR header or any user/identity.. + */ + it('should deny all requests', function() { + + }) + }) + +}) + +describe('when the users is in the Thali_Pull_Replication role', function() { + describe('When the Database name is "foobar".', function() { + describe('for the following paths and verbs', function() { + /** https://wiki.apache.org/couchdb/HTTP_Document_API + | Path | Method | PouchDB API | + * |------ |-------- |-------------| + * | / | GET | NA [3] | + ** a PUT to / is an add/update of a :db. + ** DB spedific paths + * | /:db | GET *POST | info | + * ** Note missing PUT & POST^ for new records ^ + * ** a PUT adds a new document or updates existing; DELETE also; POST adds as well. + + ** | /:db/_all_docs | GET, HEAD#?, POST [1] | allDocs | + ** | /:db/_changes | GET, POST [2] | changes | + * | /:db/_bulk_get #? | POST | bulkGet | + * | /:db/_revs_diff | POST | revsDiff | + ** DB - resources + * | /:db/:id | GET *PUT | get | + * | /:db/:id/attachment | GET *PUT, DELETE | get & getAttachment | + * + * * HEAD is used to retrieve basic infomration; PUT or POST can create a document, with PUT the document ID is part of the path (:id) + * * POST to a /:db path creates a new DOC. see /:db above. + ** What is this? ** + * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | + * | /:db/_local/:id + * + * :db - Substitute with the name of the DB we are protecting. + * :id - Substitute with the ID of a document as requested over the wire. + * thali_:id - Is an ID that begins with the prefix thali_ and otherwise + * is just an ID. + */ + + + /** + * note: http://stackoverflow.com/questions/4038885/how-to-design-a-string-matching-algorithm-where-the-input-is-the-exact-string-an + * https://github.com/isaacs/minimatch + * https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm + * https://en.wikipedia.org/wiki/String_searching_algorithm#Single_pattern_algorithms + * http://www-igm.univ-mlv.fr/~lecroq/string/index.html + * + */ + + }) + describe('and the DB name and parameter is :db == foobar', function() { + describe('and the request path IS NOT /foobar', function() { + it('should not allow /fuzbar', function() { + + }) + it('should not allow /fuzbar/', function() { + + }) + it('should not allow /fuzbar/1', function() { + + }) + }) + + describe('and the request path IS /foobar', function() { + it('should allow GET /foobar', function() { + + }) + it('should allow GET /foobar/', function() { + + }) + it('should allow GET /foobar/1', function() { + + }) + it('should allow GET /foobar/a', function() { + + }) + it('should allow GET /foobar/a/attachment', function() { + + }) + it('should NOT allow PUT, POST /foobar', function() { + + }) + it('should NOT allow PUT, POST /foobar/', function() { + + }) + it('should NOT allow PUT, POST /foobar/1', function() { + + }) + it('should NOT allow PUT, POST /foobar/a', function() { + + }) + it('should NOT allow PUT, POST /foobar/a/attachment', function() { + + }) + }) + }) + + }) + +}) + + From e6bca6c68ed48e4418cbe2699ed03011b11137ce Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 9 Mar 2016 18:26:56 -0500 Subject: [PATCH 14/19] updating acceptance tests --- newtests.txt | 42 ++++++- test/acl-block.1.js | 44 +++++++ test/handlers2.js | 21 ++++ test/test-acceptance.js | 267 +++++++++++++++++++++++++++++++++------- 4 files changed, 331 insertions(+), 43 deletions(-) create mode 100644 test/acl-block.1.js create mode 100644 test/handlers2.js diff --git a/newtests.txt b/newtests.txt index a98cc71..8816c70 100644 --- a/newtests.txt +++ b/newtests.txt @@ -26,4 +26,44 @@ * the value set in this object. * If both checks pass then we MUST call next(). Otherwise we MUST immediately * return a 401 Unauthorized and close the connection as defined below. For now - * we won't worry about also returning a www-authenticate header. \ No newline at end of file + * we won't worry about also returning a www-authenticate header. + + + /** https://wiki.apache.org/couchdb/HTTP_Document_API + | Path | Method | PouchDB API | + * |------ |-------- |-------------| + * | / | GET | NA [3] | + ** a PUT to / is an add/update of a :db. + ** DB spedific paths - note that ':db' token to be substituted with the db name in that context + * | /:db | GET | info | + ** | /:db/_all_docs | GET, HEAD, POST [1] | allDocs | + ** | /:db/_changes | GET, POST [2] | changes | + * | /:db/_bulk_get | POST | bulkGet | + * | /:db/_revs_diff | POST | revsDiff | + ** DB - resources + * | /:db/:id | GET | get | + * | /:db/:id/attachment | GET | get & getAttachment | + * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | + * + * * HEAD is used to retrieve basic infomration; PUT or POST can create a document, with PUT the document ID is part of the path (:id) + * * POST to a /:db path creates a new DOC. see /:db above. + ** What is this? ** + * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | + * | /:db/_local/:id + * + * :db - Substitute with the name of the DB we are protecting. + * :id - Substitute with the ID of a document as requested over the wire. + * thali_:id - Is an ID that begins with the prefix thali_ and otherwise + * is just an ID. + */ + + /** + * note: http://stackoverflow.com/questions/4038885/how-to-design-a-string-matching-algorithm-where-the-input-is-the-exact-string-an + * https://github.com/isaacs/minimatch + * https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm + * https://en.wikipedia.org/wiki/String_searching_algorithm#Single_pattern_algorithms + * http://www-igm.univ-mlv.fr/~lecroq/string/index.html + * + */ + + \ No newline at end of file diff --git a/test/acl-block.1.js b/test/acl-block.1.js new file mode 100644 index 0000000..0a1194e --- /dev/null +++ b/test/acl-block.1.js @@ -0,0 +1,44 @@ +"use strict"; + +//role.path.verb + + +module.exports = [{ + "role" : "user", + "paths": [ { + "path" : "/foo", + "verbs": ["GET", "POST"] + },{ + "path": "/zin", + "verbs" : ["PUT"] + } + ] +}, { + "role" : "repl", + "paths" : [{ + "path" : "/{:db}/_all_docs", + "verbs":["GET", "HEAD", "POST"] + },{ + "path" :"/{:db}/_changes", + "verbs":["GET", "POST"] + },{ + "path" :"/{:db}/_bulk_get", + "verbs": ["POST"] + }, { + "path" :"/{:db}/_revs_diff", + "verbs":["POST"] + }, { + "path" :"/{:db}/{:id}", + "verbs" :["GET"] + }, { + "path" :"/{:db}/{:id}/attachment", + "verbs":["GET"] + }, { + "path" :"/{:db}/_local/thali_{:id}", + "verbs":["GET", "PUT", "DELETE"] + }, { + "path": "/{:db}/_local/{:id}", + "verbs":["GET", "PUT", "DELETE"] + } + ] +}]; diff --git a/test/handlers2.js b/test/handlers2.js new file mode 100644 index 0000000..b31be64 --- /dev/null +++ b/test/handlers2.js @@ -0,0 +1,21 @@ +'use strict'; + +var colors = require('colors'); + +module.exports = { + get: function(req, res) { + console.log(colors.green('\tin the handler for get')); + res.status(200).json({ message: 'you made it' }); + // next(); + }, + post: function(req, res) { + console.log(colors.green('\tin the post handler')); + res.status(200).json({ name: 'you made it' }); + // next(); + }, + put: function(req, res) { + console.log(colors.green('\tin the put handler')); + res.status(200).json({ name: 'you made it' }); + // next(); + } +} \ No newline at end of file diff --git a/test/test-acceptance.js b/test/test-acceptance.js index e7434f2..5d99c9a 100644 --- a/test/test-acceptance.js +++ b/test/test-acceptance.js @@ -1,3 +1,20 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +function genericHandlers(router) { + var handlers = require('./handlers2'); + router.get('/', handlers.get); + router.post('/', handlers.post); + router.put('/', handlers.put); + return router; +} /** * we define 2 distinct 'roles' (other than Admin) @@ -5,14 +22,167 @@ * 2. Beacon - using a well-known PSK identity of beacon * All other requests are non-authenticated and purely anonymous are rejected. */ +describe('should let all through', function() { + describe(' - simple check with NO acl - no identity', function() { + var app, router; + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + //req.connection.pskIdentity = 'public';//not adding any identity for faking. + next(); + }) + //Norml middleware usage.. + //router.all('*', lib([{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be 200', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(200, done); + }) + }) + describe('simple check with empty acl - public identity', function() { + var app, router; + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) + //Norml middleware usage.. + router.all('*', lib([{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be 401', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) + }) + describe('simple check with empty - user', function() { + var app, router; + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + //Norml middleware usage.. + router.all('*', lib([{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be 401', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) + }) +}) + +//TODO: +describe('block.1 - for all roles and users - if the request is just a /', function() { + var app; + var router; + + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) + //Norml middleware usage.. + + var acl = require('./acl-block.1.js'); + + router.all('*', lib(acl)); + //mock handlers + app.use('/', genericHandlers(router)); + }) -describe('for all roles and users - if the request is just a /', function() { - it('should be 401 - unauthorized', function(done) { + it('should be 401 - unauthorized - no user', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) - done(); + it('should be 401 - unauthorized - user', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('should be 401 - unauthorized - public', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); }) }) +describe('block.2 - just a dummy sanity check - if the request is just a /', function() { + var app; + var router; + + app = express(); + router = express.Router(); + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + //req.connection.pskIdentity = 'public'; + next(); + }) + //Norml middleware usage.. + + var acl = [{ + "path": "/", + "roles": [ + {"role": "public", + "verbs": []}, + {"role": "user", + "verbs": ["POST", "PUT", "GET", "GET", "PUT", "POST"]} + ] + }]; + + router.all('*', lib(acl)); + //mock handlers + app.use('/', genericHandlers(router)); + }) + + it('dummy should be 200 - no user', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) + +}) +//TODO: describe('when the user is pskIdentity = beacon', function() { describe('and he requests any path other than the "beacon" path', function() { //todo: what is the beacon path @@ -35,48 +205,61 @@ describe('when the user is pskIdentity = beacon', function() { }) +//TODO: Bulk of work describe('when the users is in the Thali_Pull_Replication role', function() { describe('When the Database name is "foobar".', function() { describe('for the following paths and verbs', function() { - /** https://wiki.apache.org/couchdb/HTTP_Document_API - | Path | Method | PouchDB API | - * |------ |-------- |-------------| - * | / | GET | NA [3] | - ** a PUT to / is an add/update of a :db. - ** DB spedific paths - * | /:db | GET *POST | info | - * ** Note missing PUT & POST^ for new records ^ - * ** a PUT adds a new document or updates existing; DELETE also; POST adds as well. - - ** | /:db/_all_docs | GET, HEAD#?, POST [1] | allDocs | - ** | /:db/_changes | GET, POST [2] | changes | - * | /:db/_bulk_get #? | POST | bulkGet | - * | /:db/_revs_diff | POST | revsDiff | - ** DB - resources - * | /:db/:id | GET *PUT | get | - * | /:db/:id/attachment | GET *PUT, DELETE | get & getAttachment | - * - * * HEAD is used to retrieve basic infomration; PUT or POST can create a document, with PUT the document ID is part of the path (:id) - * * POST to a /:db path creates a new DOC. see /:db above. - ** What is this? ** - * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | - * | /:db/_local/:id - * - * :db - Substitute with the name of the DB we are protecting. - * :id - Substitute with the ID of a document as requested over the wire. - * thali_:id - Is an ID that begins with the prefix thali_ and otherwise - * is just an ID. - */ - - - /** - * note: http://stackoverflow.com/questions/4038885/how-to-design-a-string-matching-algorithm-where-the-input-is-the-exact-string-an - * https://github.com/isaacs/minimatch - * https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm - * https://en.wikipedia.org/wiki/String_searching_algorithm#Single_pattern_algorithms - * http://www-igm.univ-mlv.fr/~lecroq/string/index.html - * - */ + //passing + // /:db GET + //fail + // /:db PUT, POST, HEAD + it('should allow GET /:db', function() { + + }) + it('should FAIL PUT, POST, HEAD', function() { + + }) + + //passing + // /:db/_all_docs GET, HEAD, POST + //fail + // /:db/_all_docs PUT, OPTIONS + + //passing + // /:db/_changes GET, POST + //fail + // /:db/_changes PUT, HEAD, OPTIONS + + //passing + // /:db/_bulk_get POST + //fail + // /:db/_bulk_get GET, PUT, HEAD, OPTIONS + + //passing + // /:db/_revs_diff POST + //fail + // /:db/_revs_diff GET, PUT, HEAD, OPTIONS + + //passing + // /:db/:id GET + //fail + // /:db/:id POST, PUT, HEAD, OPTIONS + + //passing + // /:db/:id/attachment GET + //fail + // /:db/:id/attachment POST, PUT, HEAD, OPTIONS + + //passing + // /:db/_local/thali_:id GET, PUT, DELETE + //fail + // /:db/_local/thali_:id POST, HEAD, OPTIONS + + //passing + // /:db/_local/:id GET, PUT, DELETE + //fail + // /:db/_local/:id POST, HEAD, OPTIONS + }) describe('and the DB name and parameter is :db == foobar', function() { From f2461dff3aa19c42f18ed7b2fd82f1ed0d45d3c2 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 30 Mar 2016 18:28:23 -0400 Subject: [PATCH 15/19] updating --- .gitignore | 5 +- lib/index.1.js | 72 ++++++++++ lib/index.js | 107 ++++++++++----- .../no-test-acl-allfail-nopath.js | 0 .../no-test-acl-fatfinger.js | 0 .../no-test-acl-get-user.js | 0 .../no-test-acl-loopquerystring.js | 0 .../no-test-acl-pathtypes.js | 0 .../no-test-acl-publicall.js | 0 .../no-test-acl-querystrings.js | 0 .../no-test-acl-userall.js | 0 {test => test-old}/test-acceptance.js | 0 test/acl-block.1.js | 13 +- test/handlers2.js | 10 ++ test/template.js | 28 ++++ test/test-core-db-2.js | 80 +++++++++++ test/test-core-db.js | 125 ++++++++++++++++++ test/test-core-resourcids.js | 57 ++++++++ test/test-noaclfile.js | 98 ++++++++++++++ test/test-parmchecks.js | 41 ++++++ 20 files changed, 589 insertions(+), 47 deletions(-) create mode 100644 lib/index.1.js rename test/test-acl-allfail-nopath.js => test-old/no-test-acl-allfail-nopath.js (100%) rename test/test-acl-fatfinger.js => test-old/no-test-acl-fatfinger.js (100%) rename test/test-acl-get-user.js => test-old/no-test-acl-get-user.js (100%) rename test/test-acl-loopquerystring.js => test-old/no-test-acl-loopquerystring.js (100%) rename test/test-acl-pathtypes.js => test-old/no-test-acl-pathtypes.js (100%) rename test/test-acl-publicall.js => test-old/no-test-acl-publicall.js (100%) rename test/test-acl-querystrings.js => test-old/no-test-acl-querystrings.js (100%) rename test/test-acl-userall.js => test-old/no-test-acl-userall.js (100%) rename {test => test-old}/test-acceptance.js (100%) create mode 100644 test/template.js create mode 100644 test/test-core-db-2.js create mode 100644 test/test-core-db.js create mode 100644 test/test-core-resourcids.js create mode 100644 test/test-noaclfile.js create mode 100644 test/test-parmchecks.js diff --git a/.gitignore b/.gitignore index 868c657..bd66e88 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,10 @@ node_modules # Optional REPL history .node_repl_history +# VSCode +rowse.VC.db + # pouchdb db/* -sample/db/* \ No newline at end of file +sample/db/* diff --git a/lib/index.1.js b/lib/index.1.js new file mode 100644 index 0000000..b2f5738 --- /dev/null +++ b/lib/index.1.js @@ -0,0 +1,72 @@ +'use strict'; + +var debug = require('debug')('thalisalti:acl'); +var fast = require('./indexOf'); + +var anonymous = 'public'; +var NOTFOUND = -1; + +/** + * Acl + * @typedef {Object} Salti.Acl + * TODO: + */ + + +/** + * Implements an Express middleware Authentication Filter + * that validates an identity provided as a property on the request.connection object + * against ACL represented as a json object. + * @param {Salti.Acl} acl - acl represented as an array of path.role.verb + * @returns {Function} returns a handler of (req, res, next) to the middleware pipeline + */ +module.exports = function (acl) { + //path.role.verb + var msg401 = { success: false, message: 'Unauthorized' }; + + return function (req, res, next) { + debug('method: %s url: %s path: %s', req.method, req.url, req.path); + + //using headers for validation of the acl engine + var identity = req.connection.pskIdentity || anonymous; + debug('Identity: %s', identity); + + //if the path is just '/' then use that, otherwise, find the path + //without the trailing '/' + var path = req.path === '/' ? '/' + : req.path.slice(-1) === '/' ? req.path.slice(0, -1) + : req.path; + + var pathExists = fast.indexOf(acl, path, 'path'); + + //here we're looking for an EXACT match as is - with full path and NO resource on the end... + //ie. /foobar/myresource.js - this will fail if /foobar is in the ACL's + + if (NOTFOUND === pathExists) { + debug('unauthorized1: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } + + //roles are an array of objects with properyt of 'role' + var roles = acl[pathExists].roles; + var roleExists = fast.indexOf(roles, identity, 'role'); + if (NOTFOUND === roleExists) { + debug('unauthorized2: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } + + //verbs are just an array of strings + var verbs = roles[roleExists].verbs; + var verbExists = fast.indexOf(verbs, req.method); + + debug('path/role: %s %s', pathExists, roleExists); + + if (NOTFOUND === verbExists) { + debug('unauthorized3: %s : %s', identity, req.path); + return res.status(401).send(msg401); + } + + //all good... + next(); + }; +}; diff --git a/lib/index.js b/lib/index.js index b2f5738..b0790fd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,7 +3,7 @@ var debug = require('debug')('thalisalti:acl'); var fast = require('./indexOf'); -var anonymous = 'public'; +var noRole = 'public'; var NOTFOUND = -1; /** @@ -12,61 +12,96 @@ var NOTFOUND = -1; * TODO: */ - /** * Implements an Express middleware Authentication Filter * that validates an identity provided as a property on the request.connection object * against ACL represented as a json object. - * @param {Salti.Acl} acl - acl represented as an array of path.role.verb + * @param {String} dbname - dbname known to provider + * @param {Salti.Acl} inAcl - acl represented as an array of path.role.verb * @returns {Function} returns a handler of (req, res, next) to the middleware pipeline */ -module.exports = function (acl) { +module.exports = function(dbname, inAcl) { + + if (!dbname || typeof dbname !== 'string') { + throw new Error('invalid configuration - missing dbname'); + } + + if (!inAcl || !(inAcl instanceof Array)) { + throw new Error('invalid configuration - inAcl is not an array'); + } + + var acl = JSON.parse(JSON.stringify(inAcl).replace(/{:db}/g, dbname)); + //path.role.verb var msg401 = { success: false, message: 'Unauthorized' }; - return function (req, res, next) { + return function(req, res, next) { debug('method: %s url: %s path: %s', req.method, req.url, req.path); - - //using headers for validation of the acl engine - var identity = req.connection.pskIdentity || anonymous; - debug('Identity: %s', identity); + + //using headers for validation of the acl engine + var pskRole = req.connection.pskRole || noRole; + debug('pskRole: %s', pskRole); //if the path is just '/' then use that, otherwise, find the path - //without the trailing '/' - var path = req.path === '/' ? '/' - : req.path.slice(-1) === '/' ? req.path.slice(0, -1) - : req.path; - - var pathExists = fast.indexOf(acl, path, 'path'); - - //here we're looking for an EXACT match as is - with full path and NO resource on the end... - //ie. /foobar/myresource.js - this will fail if /foobar is in the ACL's - - if (NOTFOUND === pathExists) { - debug('unauthorized1: %s : %s', identity, req.path); - return res.status(401).send(msg401); - } + // //without the trailing '/' + // var path = req.path === '/' ? '/' + // : req.path.slice(-1) === '/' ? req.path.slice(0, -1) + // : req.path; + + var roleExists = fast.indexOf(acl, pskRole, 'role'); - //roles are an array of objects with properyt of 'role' - var roles = acl[pathExists].roles; - var roleExists = fast.indexOf(roles, identity, 'role'); if (NOTFOUND === roleExists) { - debug('unauthorized2: %s : %s', identity, req.path); + debug('unauthorized1: %s : %s', pskRole, req.path); return res.status(401).send(msg401); } - //verbs are just an array of strings - var verbs = roles[roleExists].verbs; - var verbExists = fast.indexOf(verbs, req.method); + var paths = acl[roleExists].paths; + var pathExists = fast.indexOf(paths, req.path, 'path'); + + if (NOTFOUND !== pathExists) { + //verbs are just an array of strings + var verbs = paths[pathExists].verbs; + var verbExists = fast.indexOf(verbs, req.method); - debug('path/role: %s %s', pathExists, roleExists); + if (NOTFOUND === verbExists) { + debug('unauthorized3: %s : %s', pskRole, req.path); + return res.status(401).send(msg401); + } + + //all good... + next(); + } + else { + //simple regex on /dbname/word + // /dbname/_local/thali_WORD + + /** Here's the rules - + * "path" :"/{:db}/{:id}", + "verbs" :["GET"] + }, { + "path" :"/{:db}/{:id}/attachment", + "verbs":["GET"] + }, { + "path" :"/{:db}/_local/thali_{:id}", - this is semantically the same as the following /_local/id + "verbs":["GET", "PUT", "DELETE"] + }, { + "path": "/{:db}/_local/{:id}", + "verbs":["GET", "PUT", "DELETE"] + } + */ + + + //where into the special resource ID paths here... + var path = req.path === '/' ? '/' + : req.path.slice(-1) === '/' ? req.path.slice(0, -1) + : req.path; - if (NOTFOUND === verbExists) { - debug('unauthorized3: %s : %s', identity, req.path); + debug('new path: %s', path); + + debug('unauthorized2: %s : %s', pskRole, req.path); return res.status(401).send(msg401); } - - //all good... - next(); + + }; }; diff --git a/test/test-acl-allfail-nopath.js b/test-old/no-test-acl-allfail-nopath.js similarity index 100% rename from test/test-acl-allfail-nopath.js rename to test-old/no-test-acl-allfail-nopath.js diff --git a/test/test-acl-fatfinger.js b/test-old/no-test-acl-fatfinger.js similarity index 100% rename from test/test-acl-fatfinger.js rename to test-old/no-test-acl-fatfinger.js diff --git a/test/test-acl-get-user.js b/test-old/no-test-acl-get-user.js similarity index 100% rename from test/test-acl-get-user.js rename to test-old/no-test-acl-get-user.js diff --git a/test/test-acl-loopquerystring.js b/test-old/no-test-acl-loopquerystring.js similarity index 100% rename from test/test-acl-loopquerystring.js rename to test-old/no-test-acl-loopquerystring.js diff --git a/test/test-acl-pathtypes.js b/test-old/no-test-acl-pathtypes.js similarity index 100% rename from test/test-acl-pathtypes.js rename to test-old/no-test-acl-pathtypes.js diff --git a/test/test-acl-publicall.js b/test-old/no-test-acl-publicall.js similarity index 100% rename from test/test-acl-publicall.js rename to test-old/no-test-acl-publicall.js diff --git a/test/test-acl-querystrings.js b/test-old/no-test-acl-querystrings.js similarity index 100% rename from test/test-acl-querystrings.js rename to test-old/no-test-acl-querystrings.js diff --git a/test/test-acl-userall.js b/test-old/no-test-acl-userall.js similarity index 100% rename from test/test-acl-userall.js rename to test-old/no-test-acl-userall.js diff --git a/test/test-acceptance.js b/test-old/test-acceptance.js similarity index 100% rename from test/test-acceptance.js rename to test-old/test-acceptance.js diff --git a/test/acl-block.1.js b/test/acl-block.1.js index 0a1194e..0fb8ffb 100644 --- a/test/acl-block.1.js +++ b/test/acl-block.1.js @@ -4,18 +4,11 @@ module.exports = [{ - "role" : "user", - "paths": [ { - "path" : "/foo", - "verbs": ["GET", "POST"] - },{ - "path": "/zin", - "verbs" : ["PUT"] - } - ] -}, { "role" : "repl", "paths" : [{ + "path": "/{:db}", + "verbs" :["GET"] + },{ "path" : "/{:db}/_all_docs", "verbs":["GET", "HEAD", "POST"] },{ diff --git a/test/handlers2.js b/test/handlers2.js index b31be64..af96c14 100644 --- a/test/handlers2.js +++ b/test/handlers2.js @@ -17,5 +17,15 @@ module.exports = { console.log(colors.green('\tin the put handler')); res.status(200).json({ name: 'you made it' }); // next(); + }, + head: function(req, res) { + console.log(colors.green('\tin the head handler')); + res.status(200).json({ name: 'you made it' }); + // next(); + }, + options: function(req, res) { + console.log(colors.green('\tin the options handler')); + res.status(200).json({ name: 'you made it' }); + // next(); } } \ No newline at end of file diff --git a/test/template.js b/test/template.js new file mode 100644 index 0000000..ebbd687 --- /dev/null +++ b/test/template.js @@ -0,0 +1,28 @@ +/* + +describe('{}}', function() { + describe(' - {}}', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + //req.connection.pskIdentity = 'public';//not adding any identity for faking. + next(); + }) + //Norml middleware usage.. + //router.all('*', lib([{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be {}}', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(200, done); + }) + }) +}) + +*/ \ No newline at end of file diff --git a/test/test-core-db-2.js b/test/test-core-db-2.js new file mode 100644 index 0000000..399361a --- /dev/null +++ b/test/test-core-db-2.js @@ -0,0 +1,80 @@ +'use strict'; + +/** this will check the following: + ** DB spedific paths - note that ':db' token to be substituted with the db name in that context + ** | /:db/_all_docs | GET, HEAD, POST [1] | allDocs | + ** | /:db/_changes | GET, POST [2] | changes | + * | /:db/_bulk_get | POST | bulkGet | + * | /:db/_revs_diff | POST | revsDiff | +*/ + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); +var dbName = 'foobar'; +var path = '/' + dbName + '/_all_docs'; + +function genericHandlers(router, path) { + var handlers = require('./handlers2'); + router.get(path, handlers.get); + router.post(path, handlers.post); + router.put(path, handlers.put); + router.head(path, handlers.head); + router.options(path, handlers.options); + return router; +} + +describe('test-core-db.js - calling the /db path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage..0 + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, path)); + }) + + it('GET should be 200', function(done) { + request(app) + .get(path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('PUT should be 401', function(done) { + request(app) + .put(path) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('POST should be 200', function(done) { + request(app) + .post(path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('HEAD should be 200', function(done) { + request(app) + .head(path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('OPTION should be 401', function(done) { + request(app) + .options(path) + .set('Accept', 'application/json') + .expect(401, done); + }) + }) +}) + diff --git a/test/test-core-db.js b/test/test-core-db.js new file mode 100644 index 0000000..f34e5aa --- /dev/null +++ b/test/test-core-db.js @@ -0,0 +1,125 @@ +'use strict'; + +/** this will check the following: + ** DB spedific paths - note that ':db' token to be substituted with the db name in that context + * | /:db | GET | info | +*/ + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +function genericHandlers(router, dbname) { + var handlers = require('./handlers2'); + router.get('/' + dbname, handlers.get); + router.post('/' + dbname, handlers.post); + router.put('/' + dbname, handlers.put); + router.head('/' + dbname, handlers.head); + router.options('/' + dbname, handlers.options); + return router; +} + +describe('test-core-db.js - calling the /db path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + var dbName = 'foobar'; + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage.. + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, dbName)); + }) + it('GET should be 200', function(done) { + request(app) + .get('/' + dbName) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('PUT should be 401', function(done) { + request(app) + .put('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('POST should be 401', function(done) { + request(app) + .post('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('HEAD should be 401', function(done) { + request(app) + .head('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('OPTION should be 401', function(done) { + request(app) + .options('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + + }) +}) + + +describe('test-core-db.js - calling the /WRONG db path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + var dbName = 'foobarbad'; + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage.. + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, dbName)); + }) + it('GET should be 401', function(done) { + request(app) + .get('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('PUT should be 401', function(done) { + request(app) + .put('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('POST should be 401', function(done) { + request(app) + .post('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('HEAD should be 401', function(done) { + request(app) + .head('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('OPTION should be 401', function(done) { + request(app) + .options('/' + dbName) + .set('Accept', 'application/json') + .expect(401, done); + }) + + }) +}) diff --git a/test/test-core-resourcids.js b/test/test-core-resourcids.js new file mode 100644 index 0000000..ae4aab8 --- /dev/null +++ b/test/test-core-resourcids.js @@ -0,0 +1,57 @@ + +/** + * * | /:db/:id | GET | get | + * | /:db/:id/attachment | GET | get & getAttachment | + * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | + */ + + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); +var dbName = 'foobar'; +var path = '/' + dbName + '/_all_docs'; + +function genericHandlers(router, path) { + var handlers = require('./handlers2'); + router.get('/' + dbName + '/1234', handlers.get); + return router; +} + +describe('test-core-resourceids - calling the /db/id path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage..0 + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, path)); + }) + + it('GET should be 200', function(done) { + request(app) + .get('/' + dbName + '/1234') + .set('Accept', 'application/json') + .expect(200, done); + }) + it('GET should be 401', function(done) { + request(app) + .get('/' + dbName + '/1234/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + + }) +}) + diff --git a/test/test-noaclfile.js b/test/test-noaclfile.js new file mode 100644 index 0000000..defe0c3 --- /dev/null +++ b/test/test-noaclfile.js @@ -0,0 +1,98 @@ +'use strict'; + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +function genericHandlers(router) { + var handlers = require('./handlers2'); + router.get('/', handlers.get); + router.post('/', handlers.post); + router.put('/', handlers.put); + return router; +} + +/** + * we define 2 distinct 'roles' (other than Admin) + * 1. Pull Replication + * 2. Beacon - using a well-known PSK identity of beacon + * All other requests are non-authenticated and purely anonymous are rejected. + */ +describe('test-noaclfile.js - should let all through', function() { + describe(' - simple check with NO acl - no identity', function() { + var app, router; + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + //req.connection.pskIdentity = 'public';//not adding any identity for faking. + next(); + }) + //Norml middleware usage.. + //router.all('*', lib([{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be 200', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(200, done); + }) + }) + describe('simple check with empty acl - public identity', function() { + var app, router; + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskIdentity = 'public'; + next(); + }) + //Norml middleware usage.. + router.all('*', lib('foobar', [{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be 401', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) + }) + describe('simple check with empty - user', function() { + var app, router; + app = express(); + router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskIdentity = 'user'; + next(); + }) + //Norml middleware usage.. + router.all('*', lib('foobar', [{}])); + //mock handlers + app.use('/', genericHandlers(router)); + }) + it('should be 401', function(done) { + request(app) + .get('/') + .send({ email: 'test@test.com', password: 'password' }) + .set('Accept', 'application/json') + .expect(401, done); + }) + }) +}) \ No newline at end of file diff --git a/test/test-parmchecks.js b/test/test-parmchecks.js new file mode 100644 index 0000000..534e1a0 --- /dev/null +++ b/test/test-parmchecks.js @@ -0,0 +1,41 @@ +'use strict'; + +/** This will do some basic parm checking */ + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); + +function genericHandlers(router) { + var handlers = require('./handlers2'); + router.get('/', handlers.get); + router.post('/', handlers.post); + router.put('/', handlers.put); + return router; +} + +describe('some parm checking', function() { + it('should throw exception if just 1 parm or arg1 is not a string', function(done) { + assert.throws(function() { + var tv = lib([]); + }); + done(); + + }) + it('should throw exception when 2nd parm is not an array', function(done) { + assert.throws(function() { + var tv = lib('foobar', 'notarray'); + }); + done(); + + }) + it('should NOT throw exception as parm 1 is string parm 2 is array', function(done) { + var tv = lib('foobar', ['foo', 'bar']); + done(); + + }) +}) From 712d0c9ed91cc0ede6479e612062c6e75c42eb09 Mon Sep 17 00:00:00 2001 From: cicorias Date: Mon, 4 Apr 2016 21:09:42 -0400 Subject: [PATCH 16/19] stuff --- lib/index.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index b0790fd..2bd447d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -75,8 +75,52 @@ module.exports = function(dbname, inAcl) { //simple regex on /dbname/word // /dbname/_local/thali_WORD - /** Here's the rules - - * "path" :"/{:db}/{:id}", + /** + * get the lastindex of '/' then it should match either + * /db or /db/local + * + * if /db + * match /{:db}/id GET + * match /{:db}/id/attachment GET + * + * \/foobar\/([^_](.*\/attachment|.*)) + * + * + * else /db/local + * + * simple.. + * + * (\/foobar\/_local\/([^\/]*)) + * + * working: ([^/]+)/?(.*)$ + * + * + * \/foobar\/_local\/(.+)(?!\/) + * + * this will match and 2nd group check for xlashes + * **** + * + * (\/foobar\/_local\/)(.+) + * + * (\/foobar\/_local\/)(.*(?!\/)) + * + * + * (\/foobar\/_local\/)((?!\/).+) + * (\/foobar\/_local\/)((?!\/).+) + * + * + * ////\/foobar\/_local\/((?!\/).+) + * + * since .../thali_ and id are same GET/PUT/DELETE just do + * /db/local/id + * + * + */ + + + + /** Here's the rules - +* "path" :"/{:db}/{:id}", "verbs" :["GET"] }, { "path" :"/{:db}/{:id}/attachment", From ccaf0f198615bcd8493604c5699b3e1e45e2340b Mon Sep 17 00:00:00 2001 From: cicorias Date: Tue, 5 Apr 2016 17:30:26 -0400 Subject: [PATCH 17/19] updates for working version --- lib/index.js | 168 +++++++++--------- test/acl-block.1.js | 42 ++--- test/handlers2.js | 7 +- ...test-core-db-2.js => test-core-alldocs.js} | 7 +- test/test-core-attachment.js | 85 +++++++++ ...ore-resourcids.js => test-core-bulkget.js} | 25 +-- test/test-core-resources-local.js | 90 ++++++++++ test/test-core-resources.js | 86 +++++++++ 8 files changed, 390 insertions(+), 120 deletions(-) rename test/{test-core-db-2.js => test-core-alldocs.js} (86%) create mode 100644 test/test-core-attachment.js rename test/{test-core-resourcids.js => test-core-bulkget.js} (67%) create mode 100644 test/test-core-resources-local.js create mode 100644 test/test-core-resources.js diff --git a/lib/index.js b/lib/index.js index 2bd447d..7237974 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,10 +1,35 @@ 'use strict'; +var assert = require('assert'); var debug = require('debug')('thalisalti:acl'); var fast = require('./indexOf'); var noRole = 'public'; var NOTFOUND = -1; +var IDTOKEN = '{:id}'; + + +function lookupPathVerb(req, paths, lookupPath) { + debug('looking up: %s : %s', lookupPath); + var pathExists = fast.indexOf(paths, lookupPath, 'path'); + debug(pathExists); + if (NOTFOUND !== pathExists) { + //verbs are just an array of strings + var verbs = paths[pathExists].verbs; + debug('verbs: %s', JSON.stringify(verbs)); + var verbExists = fast.indexOf(verbs, req.method); + debug(verbExists); + + if (NOTFOUND === verbExists) { + debug('verb and path not found: req.Path: %s lookupPath: %s', req.path, lookupPath); + return false; + } + return true; + } + else { + return false; + } +} /** * Acl @@ -36,18 +61,13 @@ module.exports = function(dbname, inAcl) { var msg401 = { success: false, message: 'Unauthorized' }; return function(req, res, next) { + var okPathVerb = false; debug('method: %s url: %s path: %s', req.method, req.url, req.path); //using headers for validation of the acl engine var pskRole = req.connection.pskRole || noRole; debug('pskRole: %s', pskRole); - //if the path is just '/' then use that, otherwise, find the path - // //without the trailing '/' - // var path = req.path === '/' ? '/' - // : req.path.slice(-1) === '/' ? req.path.slice(0, -1) - // : req.path; - var roleExists = fast.indexOf(acl, pskRole, 'role'); if (NOTFOUND === roleExists) { @@ -56,96 +76,80 @@ module.exports = function(dbname, inAcl) { } var paths = acl[roleExists].paths; - var pathExists = fast.indexOf(paths, req.path, 'path'); + + //here we're doing a strict simple RAW path lookup on ACLs + var rawExists = fast.indexOf(paths, req.path, 'path'); - if (NOTFOUND !== pathExists) { + if (NOTFOUND !== rawExists) { //verbs are just an array of strings - var verbs = paths[pathExists].verbs; + var verbs = paths[rawExists].verbs; var verbExists = fast.indexOf(verbs, req.method); if (NOTFOUND === verbExists) { debug('unauthorized3: %s : %s', pskRole, req.path); return res.status(401).send(msg401); } - - //all good... - next(); + else { + okPathVerb = true; + } } else { - //simple regex on /dbname/word - // /dbname/_local/thali_WORD - - /** - * get the lastindex of '/' then it should match either - * /db or /db/local - * - * if /db - * match /{:db}/id GET - * match /{:db}/id/attachment GET - * - * \/foobar\/([^_](.*\/attachment|.*)) - * - * - * else /db/local - * - * simple.. - * - * (\/foobar\/_local\/([^\/]*)) - * - * working: ([^/]+)/?(.*)$ - * - * - * \/foobar\/_local\/(.+)(?!\/) - * - * this will match and 2nd group check for xlashes - * **** - * - * (\/foobar\/_local\/)(.+) - * - * (\/foobar\/_local\/)(.*(?!\/)) - * - * - * (\/foobar\/_local\/)((?!\/).+) - * (\/foobar\/_local\/)((?!\/).+) - * - * - * ////\/foobar\/_local\/((?!\/).+) - * - * since .../thali_ and id are same GET/PUT/DELETE just do - * /db/local/id - * - * - */ - - - - /** Here's the rules - -* "path" :"/{:db}/{:id}", - "verbs" :["GET"] - }, { - "path" :"/{:db}/{:id}/attachment", - "verbs":["GET"] - }, { - "path" :"/{:db}/_local/thali_{:id}", - this is semantically the same as the following /_local/id - "verbs":["GET", "PUT", "DELETE"] - }, { - "path": "/{:db}/_local/{:id}", - "verbs":["GET", "PUT", "DELETE"] - } - */ + //this section deals with /db/id, /db/_local/id, and /db/id/attachment stuff. + var pathParts = req.path.split('/'); - - //where into the special resource ID paths here... - var path = req.path === '/' ? '/' - : req.path.slice(-1) === '/' ? req.path.slice(0, -1) - : req.path; - - debug('new path: %s', path); + if (pathParts[0] !== '') { + //sanity check.. the first element should always be empty. + debug('unauthorized4.0: %s : %s', pskRole, req.path); + return res.status(401).send(msg401); + } - debug('unauthorized2: %s : %s', pskRole, req.path); - return res.status(401).send(msg401); + var lookupPath = req.path.substring(0, req.path.lastIndexOf('/') + 1) + IDTOKEN; + + if (pathParts.length === 3) { + //we have just a path and ID + //do a search on /db/id and bail if NO GOOD + if (!lookupPathVerb(req, paths, lookupPath)) { + debug('unauthorized4.1: req.Path: %s req.path: %s', req.path, lookupPath); + return res.status(401).send(msg401); + } + else { + okPathVerb = true; + } + } + else if (pathParts.length === 4 && pathParts[2] === '_local') { + debug('pathParts.length: %s pathParts[2]: %s', pathParts.length, pathParts[2]); + //it's either _local or id/attachment + if (!lookupPathVerb(req, paths, lookupPath)) { + debug('unauthorized4.2: req.Path: %s req.path: %s', req.path, lookupPath); + return res.status(401).send(msg401); + } + else { + debug('this should be authorized..'); + okPathVerb = true; + } + } + else if (pathParts.length === 4 && pathParts[3] === 'attachment') { + var attachmentPath = '/' + pathParts[1] + '/' + IDTOKEN + '/attachment'; + + if (!lookupPathVerb(req, paths, attachmentPath)) { + debug('unauthorized4.3: req.Path: %s req.path: %s', req.path, attachmentPath); + return res.status(401).send(msg401); + } + else { + okPathVerb = true; + } + } + else { + debug('unauthorized5: %s : %s', pskRole, req.path); + return res.status(401).send(msg401); + } } + //might want to check if isvalid true too. + //All good - next in pipeline + debug('outside all ifs'); + assert.ok(okPathVerb); + next(); }; }; diff --git a/test/acl-block.1.js b/test/acl-block.1.js index 0fb8ffb..e7c1b83 100644 --- a/test/acl-block.1.js +++ b/test/acl-block.1.js @@ -1,37 +1,37 @@ -"use strict"; +'use strict'; //role.path.verb module.exports = [{ - "role" : "repl", - "paths" : [{ - "path": "/{:db}", - "verbs" :["GET"] + 'role' : 'repl', + 'paths' : [{ + 'path': '/{:db}', + 'verbs' :['GET'] },{ - "path" : "/{:db}/_all_docs", - "verbs":["GET", "HEAD", "POST"] + 'path' : '/{:db}/_all_docs', + 'verbs':['GET', 'HEAD', 'POST'] },{ - "path" :"/{:db}/_changes", - "verbs":["GET", "POST"] + 'path' :'/{:db}/_changes', + 'verbs':['GET', 'POST'] },{ - "path" :"/{:db}/_bulk_get", - "verbs": ["POST"] + 'path' :'/{:db}/_bulk_get', + 'verbs': ['POST'] }, { - "path" :"/{:db}/_revs_diff", - "verbs":["POST"] + 'path' :'/{:db}/_revs_diff', + 'verbs':['POST'] }, { - "path" :"/{:db}/{:id}", - "verbs" :["GET"] + 'path' :'/{:db}/{:id}', + 'verbs' :['GET'] }, { - "path" :"/{:db}/{:id}/attachment", - "verbs":["GET"] + 'path' :'/{:db}/{:id}/attachment', + 'verbs':['GET'] }, { - "path" :"/{:db}/_local/thali_{:id}", - "verbs":["GET", "PUT", "DELETE"] + 'path' :'/{:db}/_local/{:id}', + 'verbs':['GET', 'PUT', 'DELETE'] }, { - "path": "/{:db}/_local/{:id}", - "verbs":["GET", "PUT", "DELETE"] + 'path': '/{:db}/_local/thali_{:id}', + 'verbs':['GET', 'PUT', 'DELETE'] } ] }]; diff --git a/test/handlers2.js b/test/handlers2.js index af96c14..6820321 100644 --- a/test/handlers2.js +++ b/test/handlers2.js @@ -20,12 +20,17 @@ module.exports = { }, head: function(req, res) { console.log(colors.green('\tin the head handler')); - res.status(200).json({ name: 'you made it' }); + res.status(200);//.json({ name: 'you made it' }); // next(); }, options: function(req, res) { console.log(colors.green('\tin the options handler')); res.status(200).json({ name: 'you made it' }); // next(); + }, + delete: function(req, res) { + console.log(colors.green('\tin the delete handler')); + res.status(200).json({ name: 'you made it' }); + // next(); } } \ No newline at end of file diff --git a/test/test-core-db-2.js b/test/test-core-alldocs.js similarity index 86% rename from test/test-core-db-2.js rename to test/test-core-alldocs.js index 399361a..7c1d476 100644 --- a/test/test-core-db-2.js +++ b/test/test-core-alldocs.js @@ -3,9 +3,6 @@ /** this will check the following: ** DB spedific paths - note that ':db' token to be substituted with the db name in that context ** | /:db/_all_docs | GET, HEAD, POST [1] | allDocs | - ** | /:db/_changes | GET, POST [2] | changes | - * | /:db/_bulk_get | POST | bulkGet | - * | /:db/_revs_diff | POST | revsDiff | */ var request = require('supertest'), @@ -28,7 +25,7 @@ function genericHandlers(router, path) { return router; } -describe('test-core-db.js - calling the /db path', function() { +describe('test-core-db-2.js - calling the /db path', function() { describe('using repl identity', function() { var app, router; app = express(); router = express.Router(); @@ -63,7 +60,7 @@ describe('test-core-db.js - calling the /db path', function() { .set('Accept', 'application/json') .expect(200, done); }) - it('HEAD should be 200', function(done) { + it('HEADz should be 200', function(done) { request(app) .head(path) .set('Accept', 'application/json') diff --git a/test/test-core-attachment.js b/test/test-core-attachment.js new file mode 100644 index 0000000..d13b392 --- /dev/null +++ b/test/test-core-attachment.js @@ -0,0 +1,85 @@ +'use strict'; + +/** this will check the following: + ** DB spedific paths - note that ':db' token to be substituted with the db name in that context + ** | /:db/:id/attachment | GET, | | +*/ + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); +var dbName = 'foobar'; +var path = '/' + dbName + '/1234/attachment'; + +function genericHandlers(router, path) { + var handlers = require('./handlers2'); + router.get(path, handlers.get); + return router; +} + +describe('test-core-attachment.js - calling the /db/id/attachment path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage..0 + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, path)); + }) + + it('GET should be 200', function(done) { + request(app) + .get(path) + .set('Accept', 'application/json') + .expect(200, done); + }) + + it('POST should be 401', function(done) { + request(app) + .post(path) + .set('Accept', 'application/json') + .expect(401, done); + }) + + it('PUT should be 401', function(done) { + request(app) + .put(path) + .set('Accept', 'application/json') + .expect(401, done); + }) + + + it('GET /zzz should be 401', function(done) { + request(app) + .get(path + '/zzz') + .set('Accept', 'application/json') + .expect(401, done); + }) + + it('GET /zzz/ should be 401', function(done) { + request(app) + .get(path + '/zzz/') + .set('Accept', 'application/json') + .expect(401, done); + }) + + it('GET zzz should be 401', function(done) { + request(app) + .get(path + 'zzz') + .set('Accept', 'application/json') + .expect(401, done); + }) + + }) +}) diff --git a/test/test-core-resourcids.js b/test/test-core-bulkget.js similarity index 67% rename from test/test-core-resourcids.js rename to test/test-core-bulkget.js index ae4aab8..ca4480c 100644 --- a/test/test-core-resourcids.js +++ b/test/test-core-bulkget.js @@ -1,8 +1,6 @@ /** - * * | /:db/:id | GET | get | - * | /:db/:id/attachment | GET | get & getAttachment | - * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | + * * | /:db/_bulk_get | GET | get | */ @@ -14,15 +12,15 @@ var request = require('supertest'), var lib = require(path.join(__dirname, '../lib/index')); var dbName = 'foobar'; -var path = '/' + dbName + '/_all_docs'; +var path = '/' + dbName + '/_bulk_get'; function genericHandlers(router, path) { var handlers = require('./handlers2'); - router.get('/' + dbName + '/1234', handlers.get); + router.post(path, handlers.get); return router; } -describe('test-core-resourceids - calling the /db/id path', function() { +describe('test-core-bulk-get - calling the /db/id path', function() { describe('using repl identity', function() { var app, router; app = express(); router = express.Router(); @@ -39,19 +37,24 @@ describe('test-core-resourceids - calling the /db/id path', function() { app.use('/', genericHandlers(router, path)); }) - it('GET should be 200', function(done) { + it('POST should be 200', function(done) { request(app) - .get('/' + dbName + '/1234') + .post(path) .set('Accept', 'application/json') .expect(200, done); }) + it('GET should be 401', function(done) { request(app) - .get('/' + dbName + '/1234/1234') + .get(path) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('GET should be 401 added path', function(done) { + request(app) + .get(path + '/1234/1234') .set('Accept', 'application/json') .expect(401, done); }) - }) }) - diff --git a/test/test-core-resources-local.js b/test/test-core-resources-local.js new file mode 100644 index 0000000..69e74d6 --- /dev/null +++ b/test/test-core-resources-local.js @@ -0,0 +1,90 @@ + +/** + * | /:db/_local/:id | GET, PUT, DELETE | get, put, remove | + */ + + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); +var dbName = 'foobar'; +var path = dbName + '/_local/1234'; + +function genericHandlers(router, path) { + var handlers = require('./handlers2'); + router.get('/' + path, handlers.get); + router.put('/' + path, handlers.put); + router.delete('/' + path, handlers.delete); + return router; +} + +describe('test-core- LOCAL - calling the /db/_local/{id} path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage..0 + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, path)); + }) + + it('GET should be 200 ' + '/' + path, function(done) { + request(app) + .get('/' + path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('PUT should be 200', function(done) { + request(app) + .put('/' + path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('DELETE should be 200', function(done) { + request(app) + .delete('/' + path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('POST should be 401', function(done) { + request(app) + .post('/' + path) + .set('Accept', 'application/json') + .expect(401, done); + }) + + it('GET should be 401 on /1234/1234', function(done) { + request(app) + .get('/' + path + '/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + it('PUT should be 401', function(done) { + request(app) + .put('/' + path + '/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + it('DELETE should be 401', function(done) { + request(app) + .delete('/' + path + '/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + + + + + }) +}) diff --git a/test/test-core-resources.js b/test/test-core-resources.js new file mode 100644 index 0000000..4c517f1 --- /dev/null +++ b/test/test-core-resources.js @@ -0,0 +1,86 @@ + +/** + * | /:db/:id | GET, PUT, DELETE | get, put, remove | + */ + + +var request = require('supertest'), + express = require('express'), + path = require('path'), + colors = require('colors'), + assert = require('assert'); + +var lib = require(path.join(__dirname, '../lib/index')); +var dbName = 'foobar'; +var path = dbName + '/1234'; + +function genericHandlers(router, path) { + var handlers = require('./handlers2'); + router.get('/' + dbName + '/1234', handlers.get); + //router.put('/' + dbName + '/1234', handlers.put); + //router.delete('/' + dbName + '/1234', handlers.delete); + return router; +} + +describe('test-core- resource calling the /db/{id} path', function() { + describe('using repl identity', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage..0 + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, path)); + }) + + it('GET should be 200 ' + '/' + path, function(done) { + request(app) + .get('/' + path) + .set('Accept', 'application/json') + .expect(200, done); + }) + it('PUT should be 401', function(done) { + request(app) + .put('/' + path) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('DELETE should be 401', function(done) { + request(app) + .delete('/' + path) + .set('Accept', 'application/json') + .expect(401, done); + }) + it('POST should be 401', function(done) { + request(app) + .post('/' + path) + .set('Accept', 'application/json') + .expect(401, done); + }) + + it('GET should be 401 on /1234/1234', function(done) { + request(app) + .get('/' + path + '/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + it('PUT should be 401', function(done) { + request(app) + .put('/' + path + '/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + it('DELETE should be 401', function(done) { + request(app) + .delete('/' + path + '/1234') + .set('Accept', 'application/json') + .expect(401, done); + }) + }) +}) From 3ca6a95a072e7de561da8016ff5643399a2da3bc Mon Sep 17 00:00:00 2001 From: cicorias Date: Tue, 5 Apr 2016 17:38:02 -0400 Subject: [PATCH 18/19] update ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bd66e88..2675515 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,7 @@ node_modules .node_repl_history # VSCode -rowse.VC.db +browse.VC.db # pouchdb From 2f69b9082978d5eb6ebf187a9f50a1551e6dde65 Mon Sep 17 00:00:00 2001 From: cicorias Date: Wed, 6 Apr 2016 20:36:48 -0400 Subject: [PATCH 19/19] address review --- lib/index.1.js | 72 ------ lib/index.js | 57 ++--- lib/indexOf.js | 10 +- newtests.txt | 69 ------ sample/app/guid.js | 11 + sample/app/validate.js | 9 - sample/pouchdb.js | 8 - sample/routes/replicate.js | 10 - sample/runit.sh | 3 - test-old/no-test-acl-allfail-nopath.js | 184 -------------- test-old/no-test-acl-fatfinger.js | 126 ---------- test-old/no-test-acl-get-user.js | 180 -------------- test-old/no-test-acl-loopquerystring.js | 110 --------- test-old/no-test-acl-pathtypes.js | 154 ------------ test-old/no-test-acl-publicall.js | 69 ------ test-old/no-test-acl-querystrings.js | 126 ---------- test-old/no-test-acl-userall.js | 126 ---------- test-old/test-acceptance.js | 316 ------------------------ test/acl-block.1.js | 6 +- test/acl-get-multipleusers.js | 31 --- test/acl-get-user.js | 25 -- test/acl-path-types.js | 32 --- test/handlers.js | 19 -- test/template.js | 28 --- test/test-core-alldocs.js | 4 +- test/test-core-attachment.js | 4 +- test/test-core-bulkget.js | 4 +- test/test-core-resources-local.js | 4 +- test/test-core-resources.js | 29 ++- test/test-noaclfile.js | 19 +- test/test-parmchecks.js | 17 +- 31 files changed, 78 insertions(+), 1784 deletions(-) delete mode 100644 lib/index.1.js delete mode 100644 newtests.txt create mode 100644 sample/app/guid.js delete mode 100755 sample/runit.sh delete mode 100644 test-old/no-test-acl-allfail-nopath.js delete mode 100644 test-old/no-test-acl-fatfinger.js delete mode 100644 test-old/no-test-acl-get-user.js delete mode 100644 test-old/no-test-acl-loopquerystring.js delete mode 100644 test-old/no-test-acl-pathtypes.js delete mode 100644 test-old/no-test-acl-publicall.js delete mode 100644 test-old/no-test-acl-querystrings.js delete mode 100644 test-old/no-test-acl-userall.js delete mode 100644 test-old/test-acceptance.js delete mode 100644 test/acl-get-multipleusers.js delete mode 100644 test/acl-get-user.js delete mode 100644 test/acl-path-types.js delete mode 100644 test/handlers.js delete mode 100644 test/template.js diff --git a/lib/index.1.js b/lib/index.1.js deleted file mode 100644 index b2f5738..0000000 --- a/lib/index.1.js +++ /dev/null @@ -1,72 +0,0 @@ -'use strict'; - -var debug = require('debug')('thalisalti:acl'); -var fast = require('./indexOf'); - -var anonymous = 'public'; -var NOTFOUND = -1; - -/** - * Acl - * @typedef {Object} Salti.Acl - * TODO: - */ - - -/** - * Implements an Express middleware Authentication Filter - * that validates an identity provided as a property on the request.connection object - * against ACL represented as a json object. - * @param {Salti.Acl} acl - acl represented as an array of path.role.verb - * @returns {Function} returns a handler of (req, res, next) to the middleware pipeline - */ -module.exports = function (acl) { - //path.role.verb - var msg401 = { success: false, message: 'Unauthorized' }; - - return function (req, res, next) { - debug('method: %s url: %s path: %s', req.method, req.url, req.path); - - //using headers for validation of the acl engine - var identity = req.connection.pskIdentity || anonymous; - debug('Identity: %s', identity); - - //if the path is just '/' then use that, otherwise, find the path - //without the trailing '/' - var path = req.path === '/' ? '/' - : req.path.slice(-1) === '/' ? req.path.slice(0, -1) - : req.path; - - var pathExists = fast.indexOf(acl, path, 'path'); - - //here we're looking for an EXACT match as is - with full path and NO resource on the end... - //ie. /foobar/myresource.js - this will fail if /foobar is in the ACL's - - if (NOTFOUND === pathExists) { - debug('unauthorized1: %s : %s', identity, req.path); - return res.status(401).send(msg401); - } - - //roles are an array of objects with properyt of 'role' - var roles = acl[pathExists].roles; - var roleExists = fast.indexOf(roles, identity, 'role'); - if (NOTFOUND === roleExists) { - debug('unauthorized2: %s : %s', identity, req.path); - return res.status(401).send(msg401); - } - - //verbs are just an array of strings - var verbs = roles[roleExists].verbs; - var verbExists = fast.indexOf(verbs, req.method); - - debug('path/role: %s %s', pathExists, roleExists); - - if (NOTFOUND === verbExists) { - debug('unauthorized3: %s : %s', identity, req.path); - return res.status(401).send(msg401); - } - - //all good... - next(); - }; -}; diff --git a/lib/index.js b/lib/index.js index 7237974..48ad8d5 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,47 +4,28 @@ var assert = require('assert'); var debug = require('debug')('thalisalti:acl'); var fast = require('./indexOf'); -var noRole = 'public'; var NOTFOUND = -1; var IDTOKEN = '{:id}'; function lookupPathVerb(req, paths, lookupPath) { - debug('looking up: %s : %s', lookupPath); var pathExists = fast.indexOf(paths, lookupPath, 'path'); - debug(pathExists); - if (NOTFOUND !== pathExists) { - //verbs are just an array of strings - var verbs = paths[pathExists].verbs; - debug('verbs: %s', JSON.stringify(verbs)); - var verbExists = fast.indexOf(verbs, req.method); - debug(verbExists); - - if (NOTFOUND === verbExists) { - debug('verb and path not found: req.Path: %s lookupPath: %s', req.path, lookupPath); - return false; - } - return true; + + if (NOTFOUND === pathExists) { + return false; } - else { + //verbs are just an array of strings + var verbs = paths[pathExists].verbs; + var verbExists = fast.indexOf(verbs, req.method); + + if (NOTFOUND === verbExists) { + debug('verb and path not found: req.Path: %s lookupPath: %s', req.path, lookupPath); return false; } + return true; + } -/** - * Acl - * @typedef {Object} Salti.Acl - * TODO: - */ - -/** - * Implements an Express middleware Authentication Filter - * that validates an identity provided as a property on the request.connection object - * against ACL represented as a json object. - * @param {String} dbname - dbname known to provider - * @param {Salti.Acl} inAcl - acl represented as an array of path.role.verb - * @returns {Function} returns a handler of (req, res, next) to the middleware pipeline - */ module.exports = function(dbname, inAcl) { if (!dbname || typeof dbname !== 'string') { @@ -64,8 +45,12 @@ module.exports = function(dbname, inAcl) { var okPathVerb = false; debug('method: %s url: %s path: %s', req.method, req.url, req.path); - //using headers for validation of the acl engine - var pskRole = req.connection.pskRole || noRole; + if (!req.connection.pskRole) { + debug('unauthorized0: no role: %s ', req.path); + return res.status(401).send(msg401); + } + + var pskRole = req.connection.pskRole; debug('pskRole: %s', pskRole); var roleExists = fast.indexOf(acl, pskRole, 'role'); @@ -76,7 +61,7 @@ module.exports = function(dbname, inAcl) { } var paths = acl[roleExists].paths; - + //here we're doing a strict simple RAW path lookup on ACLs var rawExists = fast.indexOf(paths, req.path, 'path'); @@ -96,7 +81,7 @@ module.exports = function(dbname, inAcl) { else { //this section deals with /db/id, /db/_local/id, and /db/id/attachment stuff. var pathParts = req.path.split('/'); - + if (pathParts[0] !== '') { //sanity check.. the first element should always be empty. debug('unauthorized4.0: %s : %s', pskRole, req.path); @@ -104,7 +89,7 @@ module.exports = function(dbname, inAcl) { } var lookupPath = req.path.substring(0, req.path.lastIndexOf('/') + 1) + IDTOKEN; - + if (pathParts.length === 3) { //we have just a path and ID //do a search on /db/id and bail if NO GOOD @@ -117,8 +102,6 @@ module.exports = function(dbname, inAcl) { } } else if (pathParts.length === 4 && pathParts[2] === '_local') { - debug('pathParts.length: %s pathParts[2]: %s', pathParts.length, pathParts[2]); - //it's either _local or id/attachment if (!lookupPathVerb(req, paths, lookupPath)) { debug('unauthorized4.2: req.Path: %s req.path: %s', req.path, lookupPath); return res.status(401).send(msg401); diff --git a/lib/indexOf.js b/lib/indexOf.js index b27b31b..e2082df 100644 --- a/lib/indexOf.js +++ b/lib/indexOf.js @@ -28,15 +28,10 @@ /** * Custom indexOf implementation from fast.js. - * @module indesOf + * @module indexOf */ function compareIt(source, target) { - if (source === target) { - return true; - } - else { - return false; - } + return source === target; } /** @@ -44,7 +39,6 @@ function compareIt(source, target) { * @param {Object} subject - the source array or object. * @param {string} target - the value to lookup. * @param {string} key - an optional property value to use if using an object as subject - * @param {Number} fromIndex - where to start in the subject (offset) * @returns {Number} the index value where found or -1 if not found */ module.exports.indexOf = function fastIndexOf(subject, target, key) { diff --git a/newtests.txt b/newtests.txt deleted file mode 100644 index 8816c70..0000000 --- a/newtests.txt +++ /dev/null @@ -1,69 +0,0 @@ - - * | Path | Method | PouchDB API | - * |------|--------|-------------| - * | / | GET | NA [3] | - * | /:db | GET | info | - * | /:db/_all_docs | GET, HEAD, POST [1] | allDocs | - * | /:db/_bulk_get | POST | bulkGet | - * | /:db/_changes | GET, POST [2] | changes | - * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | - * | /:db/_local/:id | - * | /:db/_revs_diff | POST | revsDiff | - * | /:db/:id | GET | get | - - review the detailed spec - https://github.com/thaliproject/Thali_CordovaPlugin/blob/vNext/thali/NextGeneration/security/thaliACLLayer.js - - - - if no identity, verify that it's loopback address calling. - - * If req.connection.pskIdentity is null/undefined then we MUST: - * - check req.ip to make sure that the IP address is set to - * "127.0.0.1" - * - make a case sensitive string compare and make sure that - * the value of the authorization header after the "CLEAR" keyword matches - * the value set in this object. - * If both checks pass then we MUST call next(). Otherwise we MUST immediately - * return a 401 Unauthorized and close the connection as defined below. For now - * we won't worry about also returning a www-authenticate header. - - - /** https://wiki.apache.org/couchdb/HTTP_Document_API - | Path | Method | PouchDB API | - * |------ |-------- |-------------| - * | / | GET | NA [3] | - ** a PUT to / is an add/update of a :db. - ** DB spedific paths - note that ':db' token to be substituted with the db name in that context - * | /:db | GET | info | - ** | /:db/_all_docs | GET, HEAD, POST [1] | allDocs | - ** | /:db/_changes | GET, POST [2] | changes | - * | /:db/_bulk_get | POST | bulkGet | - * | /:db/_revs_diff | POST | revsDiff | - ** DB - resources - * | /:db/:id | GET | get | - * | /:db/:id/attachment | GET | get & getAttachment | - * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | - * - * * HEAD is used to retrieve basic infomration; PUT or POST can create a document, with PUT the document ID is part of the path (:id) - * * POST to a /:db path creates a new DOC. see /:db above. - ** What is this? ** - * | /:db/_local/thali_:id | GET, PUT, DELETE | get, put, remove | - * | /:db/_local/:id - * - * :db - Substitute with the name of the DB we are protecting. - * :id - Substitute with the ID of a document as requested over the wire. - * thali_:id - Is an ID that begins with the prefix thali_ and otherwise - * is just an ID. - */ - - /** - * note: http://stackoverflow.com/questions/4038885/how-to-design-a-string-matching-algorithm-where-the-input-is-the-exact-string-an - * https://github.com/isaacs/minimatch - * https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm - * https://en.wikipedia.org/wiki/String_searching_algorithm#Single_pattern_algorithms - * http://www-igm.univ-mlv.fr/~lecroq/string/index.html - * - */ - - \ No newline at end of file diff --git a/sample/app/guid.js b/sample/app/guid.js new file mode 100644 index 0000000..d0b21e4 --- /dev/null +++ b/sample/app/guid.js @@ -0,0 +1,11 @@ +'use strict'; + +module.exports = function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); +} diff --git a/sample/app/validate.js b/sample/app/validate.js index 1f152d2..70459e4 100644 --- a/sample/app/validate.js +++ b/sample/app/validate.js @@ -33,12 +33,3 @@ function genDoc(){ return JSON.stringify({ _id: guid(), "type": "foobar" }) } -function guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + - s4() + '-' + s4() + s4() + s4(); -} \ No newline at end of file diff --git a/sample/pouchdb.js b/sample/pouchdb.js index 98dc9eb..d55316e 100644 --- a/sample/pouchdb.js +++ b/sample/pouchdb.js @@ -33,11 +33,3 @@ module.exports = [{ "verbs": ["GET", "PUT", "POST"] }] }]; - - -///_uuids - -///_all_dbs -// /_utils/js/zeroclipboard/Z -//db_utils -//db/css/ diff --git a/sample/routes/replicate.js b/sample/routes/replicate.js index 556b77e..6c5f536 100644 --- a/sample/routes/replicate.js +++ b/sample/routes/replicate.js @@ -58,14 +58,4 @@ router.post('/add', function (req, res, next) { }) -function guid() { - function s4() { - return Math.floor((1 + Math.random()) * 0x10000) - .toString(16) - .substring(1); - } - return s4() + s4() + '-' + s4() + '-' + s4() + '-' + - s4() + '-' + s4() + s4() + s4(); -} - module.exports = router; diff --git a/sample/runit.sh b/sample/runit.sh deleted file mode 100755 index 9649e09..0000000 --- a/sample/runit.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -nodemon -i node_modules/ diff --git a/test-old/no-test-acl-allfail-nopath.js b/test-old/no-test-acl-allfail-nopath.js deleted file mode 100644 index 69e0a4b..0000000 --- a/test-old/no-test-acl-allfail-nopath.js +++ /dev/null @@ -1,184 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -//path.verb.role -var acl = require('./acl-get-multipleusers.js'); - -describe('acl-allfail-nopath', function () { - describe('no paths OK all should fail', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); - - }) - - describe('should throw a 401', function () { - it('when no path exists', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - }) - }) - - describe('acl get user tests - with empty identity', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = ''; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); - - }) - - describe('should throw a 401', function () { - it('when no path exists', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - }) - }) - - - - describe('acl get user tests - with good identity', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); - - }) - - describe('should throw a 401', function () { - it('when no path exists', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('when no path exists', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - }) - - }); - - -}) \ No newline at end of file diff --git a/test-old/no-test-acl-fatfinger.js b/test-old/no-test-acl-fatfinger.js deleted file mode 100644 index 02cffec..0000000 --- a/test-old/no-test-acl-fatfinger.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -//path.verb.role -var acl = require('./acl-get-multipleusers.js'); - - -describe('fatfinger - user - mixed and duplicate', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/fatfinger', handlers[0]); - router.post('/fatfinger', handlers[1]); - router.put('/fatfinger', handlers[2]); - app.use('/', router); - - }) - - describe('fatfinger verbs to /fatfinger', function () { - it('shoudl be OK - 200', function (done) { - request(app) - .get('/fatfinger') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .put('/fatfinger') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .post('/fatfinger') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - }) - -}) - -describe('fatfinger - public - no public all', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity= 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/fatfinger', handlers[0]); - router.post('/fatfinger', handlers[1]); - router.put('/fatfinger', handlers[2]); - app.use('/', router); - - }) - - describe('fatfinger - public no verbs', function () { - it('should be 401', function (done) { - request(app) - .get('/fatfinger') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('should be 401', function (done) { - request(app) - .put('/fatfinger') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('should be 401', function (done) { - request(app) - .post('/fatfinger') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - }) - -}) - diff --git a/test-old/no-test-acl-get-user.js b/test-old/no-test-acl-get-user.js deleted file mode 100644 index 4253eda..0000000 --- a/test-old/no-test-acl-get-user.js +++ /dev/null @@ -1,180 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -//path.verb.role -var acl = require('./acl-get-user.js'); - -describe('acl-get-user acl get user tests - with bad identity', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); - - }) - - describe('get put and post', function () { - it('should throw a 401', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('should throw a 401', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('should throw a 401', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - }) -}) - -describe('acl get user tests - with empty identity', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = ''; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); - - }) - - describe('get put and post', function () { - it('should throw a 401', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('should throw a 401', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - it('should throw a 401', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'public') - .expect(401, done); - }) - }) -}) - - - -describe('acl get user tests - with good identity', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/', handlers[0]); - router.post('/', handlers[1]); - router.put('/', handlers[2]); - app.use('/', router); - - }) - - describe('get put and post to a root path /', function () { - it('shoudl be OK - 200', function (done) { - request(app) - .get('/') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .put('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .post('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - }) - -}); diff --git a/test-old/no-test-acl-loopquerystring.js b/test-old/no-test-acl-loopquerystring.js deleted file mode 100644 index 49890f3..0000000 --- a/test-old/no-test-acl-loopquerystring.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict'; - -if (! process.env.TEST_LOOP ){ - console.log('not testing loops... set env:TEST_LOOP to run'); - return; -} - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); -//path.verb.role -var acl = require('./acl-get-multipleusers.js'); - -function generateString(length) { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#$%^&*()-_+=!@~<>?/.,[]}{\|}"; - for (var i = 0; i < length; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} - -var app; -var router; -var randomPaths = new Array(1000); - -var testTimeout = 20000; - -for (var i = 0; i < randomPaths.length; i++) { - var randomString = generateString(40); - randomPaths[i] = randomString; -} - -for (var i = 0; i < randomPaths.length; i++) { - var randomString = generateString(40); - randomPaths[i] = randomString; -} - - -function makeTest(path, status) { - it('testing path and should be ' + status + ' for ' + path, function (done) { - request(app) - .post(path) - .send({ message: "stuff" }) - .expect(status) - .end(done); - }); -} - -describe('acl-loopquerystrings - this is the loop for passing tests', function () { - this.timeout(testTimeout); - before(function () { - - app = express(); - router = express.Router(); - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - //Norml middleware usage.. - router.all('*', lib(acl)); - //mock handlers - var handlers = require('./handlers'); - router.get('/fatfinger', handlers[0]); - router.post('/fatfinger', handlers[1]); - router.put('/fatfinger', handlers[2]); - app.use('/', router); - - }) - - //these tests have a ? for query string. - for (var i = 0; i < randomPaths.length; i++) { - makeTest('/fatfinger?' + randomPaths[i], 200); - } - return; -}) - -describe('this is the loop for failing tests', function () { - this.timeout(testTimeout); - before(function () { - - app = express(); - router = express.Router(); - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - //Norml middleware usage.. - router.all('*', lib(acl)); - //mock handlers - var handlers = require('./handlers'); - router.get('/foo', handlers[0]); - router.post('/foo', handlers[1]); - router.put('/foo', handlers[2]); - app.use('/', router); - - }) - - // this has NO ? for query string - for (var i = 0; i < randomPaths.length; i++) { - makeTest('/foo?' + randomPaths[i], 401); - } - return; -}) diff --git a/test-old/no-test-acl-pathtypes.js b/test-old/no-test-acl-pathtypes.js deleted file mode 100644 index 282031d..0000000 --- a/test-old/no-test-acl-pathtypes.js +++ /dev/null @@ -1,154 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -var acl = require('./acl-path-types'); - - -describe('acl-pathtypes - checking for items after a base path', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/base', handlers[0]); - router.get('/base/myfile.js', handlers[0]); - router.get('/foo', handlers[0]); - router.get('/bar/foo', handlers[0]); - router.get('/foo/myfile.js', handlers[0]); - router.get('/bar/foo/myfile.js', handlers[0]); - router.get('/fiz/baz', handlers[0]); - router.get('/fiz/baz/myfile.js', handlers[0]); - app.use('/', router); - - }) - - describe('if a path ends in a /', function () { - it('should allow /base through', function (done) { - request(app) - .get('/base') - .set('Accept', 'application/json') - .expect(200, done) - }) - - it('should NOT /base/myfile.js through', function (done) { - request(app) - .get('/base/myfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - - it('should stop /basemyfile.js', function (done) { - request(app) - .get('/basemyfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - }) - - describe('if a path doesnt ends in a / - it should assume it', function () { - it('should allow /foo/ through', function (done) { - request(app) - .get('/foo/') - .set('Accept', 'application/json') - .expect(200, done) - }) - it('should NOT allow /foo/myfile.js through', function (done) { - request(app) - .get('/foo/myfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - - it('should stop /foomyfile.js', function (done) { - request(app) - .get('/foomyfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - }) - - describe('if a path ends in a /', function () { - it('should allow /bar/foo through', function (done) { - request(app) - .get('/bar/foo') - .set('Accept', 'application/json') - .expect(200, done) - }) - - it('should NOT allow /bar/foo/myfile.js through', function (done) { - request(app) - .get('/bar/foo/myfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - - it('should stop /bar/foomyfile.js', function (done) { - request(app) - .get('/bar/foomyfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - }) - - describe('if a path doesnt ends in a / - it should assume it', function () { - it('should allow /fiz/baz/ through', function (done) { - request(app) - .get('/fiz/baz/') - .set('Accept', 'application/json') - .expect(200, done) - }) - - it('should NOT allow /fiz/baz/myfile.js through', function (done) { - request(app) - .get('/fiz/baz/myfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - - it('should stop /fiz/bazmyfile.js', function (done) { - request(app) - .get('/fiz/bazmyfile.js') - .set('Accept', 'application/json') - .expect(401, done) - }) - }) - describe('few munged paths using / - ', function () { - it('should NOT allow / through', function (done) { - request(app) - .get('/ ') - .set('Accept', 'application/json') - .expect(401, done) - }) - it('should NOT allow /# through', function (done) { - request(app) - .get('/#') - .set('Accept', 'application/json') - .expect(401, done) - }) - - }) - - -}) \ No newline at end of file diff --git a/test-old/no-test-acl-publicall.js b/test-old/no-test-acl-publicall.js deleted file mode 100644 index 51f02e6..0000000 --- a/test-old/no-test-acl-publicall.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -//path.verb.role -var acl = require('./acl-get-multipleusers.js'); - -describe('acl-publicall - acl public get all; user just get', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/publicall', handlers[0]); - router.post('/publicall', handlers[1]); - router.put('/publicall', handlers[2]); - app.use('/', router); - - }) - - describe('publicall verbs to /publicall', function () { - it('shoudl be OK - 200', function (done) { - request(app) - .get('/publicall') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .put('/publicall') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .post('/publicall') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - }) - -}) - diff --git a/test-old/no-test-acl-querystrings.js b/test-old/no-test-acl-querystrings.js deleted file mode 100644 index 78a77cb..0000000 --- a/test-old/no-test-acl-querystrings.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -//path.verb.role -var acl = require('./acl-get-multipleusers.js'); - - -describe('acl-querystrings - bunch of query string junk', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/fatfinger', handlers[0]); - router.post('/fatfinger', handlers[1]); - router.put('/fatfinger', handlers[2]); - app.use('/', router); - - }) - - describe('query string junk to /fatfinger?xxxxx#dkdkdkd', function () { - it('shoudl be OK - 200', function (done) { - request(app) - .get('/fatfinger?foobar=nothing#/simplestuff') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .put('/fatfinger?foobar=nothing#/simplestuff') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .post('/fatfinger?foobar=nothing#/simplestuff') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - }) - -}) - -describe('query string junk', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity= 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/fatfinger', handlers[0]); - router.post('/fatfinger', handlers[1]); - router.put('/fatfinger', handlers[2]); - app.use('/', router); - - }) - - describe('query string junk', function () { - it('should be 401', function (done) { - request(app) - .get('/fatfinger?foobar=nothing#/simplestuff') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('should be 401', function (done) { - request(app) - .put('/fatfinger?foobar=nothing#/simplestuff') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('should be 401', function (done) { - request(app) - .post('/fatfinger?foobar=nothing#/simplestuff') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - }) - -}) - diff --git a/test-old/no-test-acl-userall.js b/test-old/no-test-acl-userall.js deleted file mode 100644 index c7722d2..0000000 --- a/test-old/no-test-acl-userall.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -//path.verb.role -var acl = require('./acl-get-multipleusers.js'); - - -describe('acl-userall - user - all', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/publicget', handlers[0]); - router.post('/publicget', handlers[1]); - router.put('/publicget', handlers[2]); - app.use('/', router); - - }) - - describe('publicget verbs to /publicget...', function () { - it('shoudl be OK - 200', function (done) { - request(app) - .get('/publicget') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .put('/publicget') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('shoudl be OK - 200', function (done) { - request(app) - .post('/publicget') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - }) - -}) - -describe('publicget - public - only get', function () { - - var app; - var router; - - before(function () { - app = express(); - router = express.Router(); - - //mocker.. - router.all('*', function (req, res, next) { - req.connection.pskIdentity= 'public'; - next(); - }) - - //Norml middleware usage.. - router.all('*', lib(acl)); - - //mock handlers - var handlers = require('./handlers'); - - router.get('/publicget', handlers[0]); - router.post('/publicget', handlers[1]); - router.put('/publicget', handlers[2]); - app.use('/', router); - - }) - - describe('publicget - just get', function () { - it('should be 200', function (done) { - request(app) - .get('/publicget') - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(200, done); - }) - it('should be 401', function (done) { - request(app) - .put('/publicget') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - it('should be 401', function (done) { - request(app) - .post('/publicget') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - // .set('Identity', 'user') - .expect(401, done); - }) - }) - -}) - diff --git a/test-old/test-acceptance.js b/test-old/test-acceptance.js deleted file mode 100644 index 5d99c9a..0000000 --- a/test-old/test-acceptance.js +++ /dev/null @@ -1,316 +0,0 @@ -'use strict'; - -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); - -var lib = require(path.join(__dirname, '../lib/index')); - -function genericHandlers(router) { - var handlers = require('./handlers2'); - router.get('/', handlers.get); - router.post('/', handlers.post); - router.put('/', handlers.put); - return router; -} - -/** - * we define 2 distinct 'roles' (other than Admin) - * 1. Pull Replication - * 2. Beacon - using a well-known PSK identity of beacon - * All other requests are non-authenticated and purely anonymous are rejected. - */ -describe('should let all through', function() { - describe(' - simple check with NO acl - no identity', function() { - var app, router; - app = express(); - router = express.Router(); - - before(function() { - //mocker.. - router.all('*', function(req, res, next) { - //req.connection.pskIdentity = 'public';//not adding any identity for faking. - next(); - }) - //Norml middleware usage.. - //router.all('*', lib([{}])); - //mock handlers - app.use('/', genericHandlers(router)); - }) - it('should be 200', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(200, done); - }) - }) - describe('simple check with empty acl - public identity', function() { - var app, router; - app = express(); - router = express.Router(); - - before(function() { - //mocker.. - router.all('*', function(req, res, next) { - req.connection.pskIdentity = 'public'; - next(); - }) - //Norml middleware usage.. - router.all('*', lib([{}])); - //mock handlers - app.use('/', genericHandlers(router)); - }) - it('should be 401', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(401, done); - }) - }) - describe('simple check with empty - user', function() { - var app, router; - app = express(); - router = express.Router(); - - before(function() { - //mocker.. - router.all('*', function(req, res, next) { - req.connection.pskIdentity = 'user'; - next(); - }) - //Norml middleware usage.. - router.all('*', lib([{}])); - //mock handlers - app.use('/', genericHandlers(router)); - }) - it('should be 401', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(401, done); - }) - }) -}) - -//TODO: -describe('block.1 - for all roles and users - if the request is just a /', function() { - var app; - var router; - - app = express(); - router = express.Router(); - - before(function() { - //mocker.. - router.all('*', function(req, res, next) { - req.connection.pskIdentity = 'public'; - next(); - }) - //Norml middleware usage.. - - var acl = require('./acl-block.1.js'); - - router.all('*', lib(acl)); - //mock handlers - app.use('/', genericHandlers(router)); - }) - - it('should be 401 - unauthorized - no user', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(401, done); - }) - - it('should be 401 - unauthorized - user', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(401, done); - }) - it('should be 401 - unauthorized - public', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(401, done); - }) -}) - -describe('block.2 - just a dummy sanity check - if the request is just a /', function() { - var app; - var router; - - app = express(); - router = express.Router(); - before(function() { - //mocker.. - router.all('*', function(req, res, next) { - //req.connection.pskIdentity = 'public'; - next(); - }) - //Norml middleware usage.. - - var acl = [{ - "path": "/", - "roles": [ - {"role": "public", - "verbs": []}, - {"role": "user", - "verbs": ["POST", "PUT", "GET", "GET", "PUT", "POST"]} - ] - }]; - - router.all('*', lib(acl)); - //mock handlers - app.use('/', genericHandlers(router)); - }) - - it('dummy should be 200 - no user', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(401, done); - }) - -}) -//TODO: -describe('when the user is pskIdentity = beacon', function() { - describe('and he requests any path other than the "beacon" path', function() { - //todo: what is the beacon path - it('POST/GET/PUT should fail with a 401', function() { - - }) - it('IF a GET should be OK 200', function() { - - }) - }) - - describe('if the user is in the Anymous role', function() { - /** - * Anonymous Role - NO CLEAR header or any user/identity.. - */ - it('should deny all requests', function() { - - }) - }) - -}) - -//TODO: Bulk of work -describe('when the users is in the Thali_Pull_Replication role', function() { - describe('When the Database name is "foobar".', function() { - describe('for the following paths and verbs', function() { - //passing - // /:db GET - //fail - // /:db PUT, POST, HEAD - it('should allow GET /:db', function() { - - }) - it('should FAIL PUT, POST, HEAD', function() { - - }) - - //passing - // /:db/_all_docs GET, HEAD, POST - //fail - // /:db/_all_docs PUT, OPTIONS - - //passing - // /:db/_changes GET, POST - //fail - // /:db/_changes PUT, HEAD, OPTIONS - - //passing - // /:db/_bulk_get POST - //fail - // /:db/_bulk_get GET, PUT, HEAD, OPTIONS - - //passing - // /:db/_revs_diff POST - //fail - // /:db/_revs_diff GET, PUT, HEAD, OPTIONS - - //passing - // /:db/:id GET - //fail - // /:db/:id POST, PUT, HEAD, OPTIONS - - //passing - // /:db/:id/attachment GET - //fail - // /:db/:id/attachment POST, PUT, HEAD, OPTIONS - - //passing - // /:db/_local/thali_:id GET, PUT, DELETE - //fail - // /:db/_local/thali_:id POST, HEAD, OPTIONS - - //passing - // /:db/_local/:id GET, PUT, DELETE - //fail - // /:db/_local/:id POST, HEAD, OPTIONS - - - }) - describe('and the DB name and parameter is :db == foobar', function() { - describe('and the request path IS NOT /foobar', function() { - it('should not allow /fuzbar', function() { - - }) - it('should not allow /fuzbar/', function() { - - }) - it('should not allow /fuzbar/1', function() { - - }) - }) - - describe('and the request path IS /foobar', function() { - it('should allow GET /foobar', function() { - - }) - it('should allow GET /foobar/', function() { - - }) - it('should allow GET /foobar/1', function() { - - }) - it('should allow GET /foobar/a', function() { - - }) - it('should allow GET /foobar/a/attachment', function() { - - }) - it('should NOT allow PUT, POST /foobar', function() { - - }) - it('should NOT allow PUT, POST /foobar/', function() { - - }) - it('should NOT allow PUT, POST /foobar/1', function() { - - }) - it('should NOT allow PUT, POST /foobar/a', function() { - - }) - it('should NOT allow PUT, POST /foobar/a/attachment', function() { - - }) - }) - }) - - }) - -}) - - diff --git a/test/acl-block.1.js b/test/acl-block.1.js index e7c1b83..423b586 100644 --- a/test/acl-block.1.js +++ b/test/acl-block.1.js @@ -5,8 +5,10 @@ module.exports = [{ 'role' : 'repl', - 'paths' : [{ - 'path': '/{:db}', + 'paths' : [ + {'path':'/', + 'verbs': ['GET'] + },{'path': '/{:db}', 'verbs' :['GET'] },{ 'path' : '/{:db}/_all_docs', diff --git a/test/acl-get-multipleusers.js b/test/acl-get-multipleusers.js deleted file mode 100644 index 709e73d..0000000 --- a/test/acl-get-multipleusers.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; - -module.exports = [ - { - "path": "/publicall", - "roles": [ - {"role": "public", - "verbs": ["GET", "POST", "PUT"]}, - {"role": "user", - "verbs": ["GET"]} - ] - }, - { - "path": "/fatfinger", - "roles": [ - {"role": "public", - "verbs": []}, - {"role": "user", - "verbs": ["POST", "PUT", "GET", "GET", "PUT", "POST"]} - ] - }, - { - "path": "/publicget", - "roles": [ - {"role": "public", - "verbs": ["GET"]}, - {"role": "user", - "verbs": ["GET", "POST", "PUT"]} - ] - } -]; diff --git a/test/acl-get-user.js b/test/acl-get-user.js deleted file mode 100644 index 441780d..0000000 --- a/test/acl-get-user.js +++ /dev/null @@ -1,25 +0,0 @@ -'use strict'; - -module.exports = [ - { - "path": '/', - "roles": [ - {"role": 'user', - "verbs": ['GET', 'PUT', 'POST']} - ] - }, - { - "path": '/foo', - "roles": [ - {"role": 'user', - "verbs": ['GET', 'PUT', 'POST']} - ] - }, - { - "path": '/bar', - "roles": [ - {"role": 'user', - "verbs": ['GET']} - ] - } -]; diff --git a/test/acl-path-types.js b/test/acl-path-types.js deleted file mode 100644 index fdfa288..0000000 --- a/test/acl-path-types.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -module.exports = [ - { - "path": '/base', - "roles": [ - {"role": 'public', - "verbs": ['GET', 'PUT', 'POST']} - ] - }, - { - "path": '/foo', - "roles": [ - {"role": 'public', - "verbs": ['GET', 'PUT', 'POST']} - ] - }, - { - "path": '/bar/foo', - "roles": [ - {"role": 'public', - "verbs": ['GET', 'PUT', 'POST']} - ] - }, - { - "path": '/fiz/baz', - "roles": [ - {"role": 'public', - "verbs": ['GET', 'PUT', 'POST']} - ] - } -]; diff --git a/test/handlers.js b/test/handlers.js deleted file mode 100644 index af665a4..0000000 --- a/test/handlers.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -var colors = require('colors'); - -module.exports = - -[function (req, res) { - console.log(colors.green('\tin the handler for get')); - res.status(200).json({ message: 'you made it' }); - // next(); -}, function (req, res) { - console.log(colors.green('\tin the post handler')); - res.status(200).json({ name: 'you made it' }); - // next(); -}, function (req, res) { - console.log(colors.green('\tin the put handler')); - res.status(200).json({ name: 'you made it' }); - // next(); -}]; diff --git a/test/template.js b/test/template.js deleted file mode 100644 index ebbd687..0000000 --- a/test/template.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - -describe('{}}', function() { - describe(' - {}}', function() { - var app, router; app = express(); router = express.Router(); - - before(function() { - //mocker.. - router.all('*', function(req, res, next) { - //req.connection.pskIdentity = 'public';//not adding any identity for faking. - next(); - }) - //Norml middleware usage.. - //router.all('*', lib([{}])); - //mock handlers - app.use('/', genericHandlers(router)); - }) - it('should be {}}', function(done) { - request(app) - .get('/') - .send({ email: 'test@test.com', password: 'password' }) - .set('Accept', 'application/json') - .expect(200, done); - }) - }) -}) - -*/ \ No newline at end of file diff --git a/test/test-core-alldocs.js b/test/test-core-alldocs.js index 7c1d476..d4cb872 100644 --- a/test/test-core-alldocs.js +++ b/test/test-core-alldocs.js @@ -7,11 +7,11 @@ var request = require('supertest'), express = require('express'), - path = require('path'), + fspath = require('path'), colors = require('colors'), assert = require('assert'); -var lib = require(path.join(__dirname, '../lib/index')); +var lib = require(fspath.join(__dirname, '../lib/index')); var dbName = 'foobar'; var path = '/' + dbName + '/_all_docs'; diff --git a/test/test-core-attachment.js b/test/test-core-attachment.js index d13b392..68b4170 100644 --- a/test/test-core-attachment.js +++ b/test/test-core-attachment.js @@ -7,11 +7,11 @@ var request = require('supertest'), express = require('express'), - path = require('path'), + fspath = require('path'), colors = require('colors'), assert = require('assert'); -var lib = require(path.join(__dirname, '../lib/index')); +var lib = require(fspath.join(__dirname, '../lib/index')); var dbName = 'foobar'; var path = '/' + dbName + '/1234/attachment'; diff --git a/test/test-core-bulkget.js b/test/test-core-bulkget.js index ca4480c..da31d8d 100644 --- a/test/test-core-bulkget.js +++ b/test/test-core-bulkget.js @@ -6,11 +6,11 @@ var request = require('supertest'), express = require('express'), - path = require('path'), + fspath = require('path'), colors = require('colors'), assert = require('assert'); -var lib = require(path.join(__dirname, '../lib/index')); +var lib = require(fspath.join(__dirname, '../lib/index')); var dbName = 'foobar'; var path = '/' + dbName + '/_bulk_get'; diff --git a/test/test-core-resources-local.js b/test/test-core-resources-local.js index 69e74d6..d7df23f 100644 --- a/test/test-core-resources-local.js +++ b/test/test-core-resources-local.js @@ -6,11 +6,11 @@ var request = require('supertest'), express = require('express'), - path = require('path'), + fspath = require('path'), colors = require('colors'), assert = require('assert'); -var lib = require(path.join(__dirname, '../lib/index')); +var lib = require(fspath.join(__dirname, '../lib/index')); var dbName = 'foobar'; var path = dbName + '/_local/1234'; diff --git a/test/test-core-resources.js b/test/test-core-resources.js index 4c517f1..3ecc459 100644 --- a/test/test-core-resources.js +++ b/test/test-core-resources.js @@ -6,22 +6,47 @@ var request = require('supertest'), express = require('express'), - path = require('path'), + fspath = require('path'), colors = require('colors'), assert = require('assert'); -var lib = require(path.join(__dirname, '../lib/index')); +var lib = require(fspath.join(__dirname, '../lib/index')); var dbName = 'foobar'; var path = dbName + '/1234'; function genericHandlers(router, path) { var handlers = require('./handlers2'); router.get('/' + dbName + '/1234', handlers.get); + router.get('/', handlers.get); //router.put('/' + dbName + '/1234', handlers.put); //router.delete('/' + dbName + '/1234', handlers.delete); return router; } +describe('test-core-resources - just the /', function() { + var app, router; app = express(); router = express.Router(); + + before(function() { + //mocker.. + router.all('*', function(req, res, next) { + req.connection.pskRole = 'repl'; + next(); + }) + //Norml middleware usage..0 + var acl = require('./acl-block.1.js'); + router.all('*', lib('foobar', acl)); + //mock handlers + app.use('/', genericHandlers(router, path)); + }) + + it('should be OK', function(done) { + request(app) + .get('/') + .set('Accept', 'application/json') + .expect(200, done); + }) +}) + describe('test-core- resource calling the /db/{id} path', function() { describe('using repl identity', function() { var app, router; app = express(); router = express.Router(); diff --git a/test/test-noaclfile.js b/test/test-noaclfile.js index defe0c3..e23927a 100644 --- a/test/test-noaclfile.js +++ b/test/test-noaclfile.js @@ -2,11 +2,11 @@ var request = require('supertest'), express = require('express'), - path = require('path'), + fspath = require('path'), colors = require('colors'), assert = require('assert'); -var lib = require(path.join(__dirname, '../lib/index')); +var lib = require(fspath.join(__dirname, '../lib/index')); function genericHandlers(router) { var handlers = require('./handlers2'); @@ -16,12 +16,6 @@ function genericHandlers(router) { return router; } -/** - * we define 2 distinct 'roles' (other than Admin) - * 1. Pull Replication - * 2. Beacon - using a well-known PSK identity of beacon - * All other requests are non-authenticated and purely anonymous are rejected. - */ describe('test-noaclfile.js - should let all through', function() { describe(' - simple check with NO acl - no identity', function() { var app, router; @@ -29,14 +23,10 @@ describe('test-noaclfile.js - should let all through', function() { router = express.Router(); before(function() { - //mocker.. router.all('*', function(req, res, next) { - //req.connection.pskIdentity = 'public';//not adding any identity for faking. next(); }) - //Norml middleware usage.. - //router.all('*', lib([{}])); - //mock handlers + app.use('/', genericHandlers(router)); }) it('should be 200', function(done) { @@ -53,14 +43,11 @@ describe('test-noaclfile.js - should let all through', function() { router = express.Router(); before(function() { - //mocker.. router.all('*', function(req, res, next) { req.connection.pskIdentity = 'public'; next(); }) - //Norml middleware usage.. router.all('*', lib('foobar', [{}])); - //mock handlers app.use('/', genericHandlers(router)); }) it('should be 401', function(done) { diff --git a/test/test-parmchecks.js b/test/test-parmchecks.js index 534e1a0..2df1730 100644 --- a/test/test-parmchecks.js +++ b/test/test-parmchecks.js @@ -2,21 +2,10 @@ /** This will do some basic parm checking */ -var request = require('supertest'), - express = require('express'), - path = require('path'), - colors = require('colors'), - assert = require('assert'); +var assert = require('assert'), + fspath = require('path'); -var lib = require(path.join(__dirname, '../lib/index')); - -function genericHandlers(router) { - var handlers = require('./handlers2'); - router.get('/', handlers.get); - router.post('/', handlers.post); - router.put('/', handlers.put); - return router; -} +var lib = require(fspath.join(__dirname, '../lib/index')); describe('some parm checking', function() { it('should throw exception if just 1 parm or arg1 is not a string', function(done) {