Skip to content
This repository was archived by the owner on Apr 25, 2018. It is now read-only.

Embrace Sanctuary #23

Merged
merged 1 commit into from
Mar 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ might some day be a userful resource to look at for documentation.
* `npm run lint`: Perform static code analysis.
* `npm run lint:fix`: Perform static code analysis and auto-fix.
* `npm run precommit`: Ready to use git precommit hook.
* `npm run sanctuary-env`: Build the sanctuary-env virtual package.
* `npm run setup`: Build virtual packages and setup git hooks.
* `npm run start`: Start the server.
* `npm run test`: Run all tests, mainly for CI usage.
* `npm run test:integration`: Run the integration tests.
Expand All @@ -83,7 +81,7 @@ might some day be a userful resource to look at for documentation.
### Application

* The http service is provided by [Express][14].
* Data manipulation utilities are provided by [Ramda][5].
* Data manipulation utilities are provided by [Sanctuary][5].
* Monadic Futures provided by [Fluture][4].
* Runtime type-checking provided by [TComb][13].
* Schema validations in the form of struct-types using [TComb Validations][15].
Expand All @@ -101,7 +99,7 @@ might some day be a userful resource to look at for documentation.
[2]: https://github.com/lorenwest/node-config/wiki
[3]: https://github.com/fantasyland/fantasy-land
[4]: https://github.com/Avaq/Fluture
[5]: http://ramdajs.com/docs
[5]: https://sanctuary.js.org/
[6]: http://mochajs.org/
[7]: http://chaijs.com/api/bdd/
[8]: http://sinonjs.org/
Expand Down
13 changes: 6 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
"lint": "eslint src test config",
"lint:fix": "npm run lint -- --fix",
"precommit": "npm run lint && npm run test:unit && npm run test:integration && npm run check-security",
"sanctuary-env": "node scripts/sanctuary-env",
"setup": "npm run sanctuary-env && git config push.followTags true",
"start": "node .",
"test": "npm run clean && npm run lint && npm run test:coverage && npm run test:integration",
"test:integration": "NODE_ENV=test mocha 'test/integration/**/*.test.js'",
Expand All @@ -25,20 +23,21 @@
"author": "PROJECT_AUTHOR",
"license": "MIT",
"dependencies": {
"bcrypt": "^0.8.7",
"bcrypt": "^1.0.1",
"body-parser": "^1.15.2",
"config": "^1.16.0",
"cookie-parser": "^1.4.3",
"express": "^4.13.3",
"fluture": "^4.0.3",
"fluture": "^5.0.0",
"http-errors": "^1.4.0",
"json5": "^0.5.0",
"jwt-simple": "^0.5.0",
"micromatch": "^2.3.11",
"momi": "^0.2.0",
"ramda": "^0.22.1",
"momi": "^0.4.0",
"request": "^2.72.0",
"sanctuary": "^0.11.1",
"sanctuary": "^0.12.2",
"sanctuary-def": "^0.9.0",
"sanctuary-type-classes": "^3.0.1",
"semver": "^5.3.0",
"tcomb": "^3.0.0",
"tcomb-validation": "^3.0.0",
Expand Down
22 changes: 0 additions & 22 deletions scripts/sanctuary-env.js

This file was deleted.

6 changes: 2 additions & 4 deletions src/actions/auth/create.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
'use strict';

const {Authentication, Authorization} = require('../../domain/models');
const {maybeToFuture} = require('../../util/future');
const validate = require('../../util/validate');
const error = require('http-errors');
const Future = require('fluture');
const bcrypt = require('bcrypt');
const {K, prop, get, fromMaybe, pipe} = require('sanctuary-env');
const {chain} = require('ramda');
const {K, prop, get, fromMaybe, pipe, maybeToFuture, chain, is} = require('../../prelude');

// invalidCredentials :: UnauthorizedError
const invalidCredentials = error(401, 'Invalid credentials');
Expand All @@ -31,7 +29,7 @@ module.exports = (req, res) => Future.do(function*() {

const [token, refresh] = yield req.services.auth.createTokenPair({
user: prop('username', user),
groups: fromMaybe([], get(Array, 'groups', user))
groups: fromMaybe([], get(is(Array), 'groups', user))
});

res.cookie('token', token, {
Expand Down
3 changes: 1 addition & 2 deletions src/actions/auth/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use strict';

const Future = require('fluture');
const {either} = require('sanctuary-env');
const {errorToJson} = require('../../util/common');
const {either, errorToJson} = require('../../prelude');

module.exports = req => Future.of(either(
error => ({authenticated: false, reason: errorToJson(error)}),
Expand Down
33 changes: 25 additions & 8 deletions src/actions/auth/session.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
'use strict';

const Future = require('fluture');
const {K, either, concat, pipe, get, maybe, at, Left, maybeToEither, or} = require('sanctuary-env');
const {chain, ifElse, test, split, trim, map} = require('ramda');
const error = require('http-errors');
const mm = require('micromatch');
const {
K,
either,
concat,
pipe,
get,
maybe,
at,
Left,
maybeToEither,
alt,
chain,
ifElse,
test,
splitOn,
trim,
map,
is
} = require('../../prelude');

// authenticatedGroups :: Array Group
const authenticatedGroups = ['@everyone', '@authenticated'];
Expand Down Expand Up @@ -41,30 +58,30 @@ const invalidAuthorizationHeader = error(400, {

// getTokenFromHeaders :: Headers -> Either Error String
const getTokenFromHeaders = pipe([
get(String, 'authorization'),
get(is(String), 'authorization'),
maybeToEither(missingAuthorizationHeader),
chain(ifElse(
test(/^ *Bearer:/),
pipe([split(':'), at(1), map(trim), maybeToEither(malformedAuthorizationHeader)]),
pipe([splitOn(':'), at(1), map(trim), maybeToEither(malformedAuthorizationHeader)]),
K(Left(invalidAuthorizationHeader))
))
]);

// getTokenFromCookies :: Cookies -> Either Error String
const getTokenFromCookies = pipe([
get(String, 'token'),
get(is(String), 'token'),
maybeToEither(missingTokenCookie)
]);

// getTokenFromRequest :: Request -> Either Error String
const getTokenFromRequest = req =>
req.method === 'GET'
? or(getTokenFromCookies(req.cookies), getTokenFromHeaders(req.headers))
? alt(getTokenFromCookies(req.cookies), getTokenFromHeaders(req.headers))
: getTokenFromHeaders(req.headers);

// getUserGroups :: User -> Array Group
const getUserGroups = pipe([
get(Array, 'groups'),
get(is(Array), 'groups'),
maybe(authenticatedGroups, concat(authenticatedGroups))
]);

Expand All @@ -81,7 +98,7 @@ module.exports = req => Future.do(function*() {
const groupsToPermissions = chain(group => grants[group] || []);

// session :: Either Error Session
const session = getTokenFromRequest(req).chain(req.services.auth.tokenToSession);
const session = chain(req.services.auth.tokenToSession, getTokenFromRequest(req));

// groups :: Array Group
const groups = getUserGroupsFromSession(session);
Expand Down
18 changes: 14 additions & 4 deletions src/actions/auth/update.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
'use strict';

const {Authorization, Session} = require('../../domain/models');
const {eitherToFuture, maybeToFuture} = require('../../util/future');
const validate = require('../../util/validate');
const Future = require('fluture');
const error = require('http-errors');
const {pipe, concat, prop, fromMaybe, get} = require('sanctuary-env');
const {chain, apply, map} = require('ramda');
const {
eitherToFuture,
maybeToFuture,
chain,
apply,
map,
pipe,
concat,
prop,
fromMaybe,
get,
is
} = require('../../prelude');

// userNotFound :: NotAuthorizedError
const userNotFound = error(403, 'User provided by token does not exist');
Expand Down Expand Up @@ -40,7 +50,7 @@ module.exports = (req, res) => Future.do(function*() {

const [token, refresh] = yield req.services.auth.createTokenPair({
user: prop('username', user),
groups: fromMaybe([], get(Array, 'groups', user))
groups: fromMaybe([], get(is(Array), 'groups', user))
});

res.cookie('token', token, {
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/auth.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

const {B, T} = require('sanctuary-env');
const {App, Middleware} = require('momi');
const {createTokenPair, tokenToSession, verifyTokenPair} = require('../services/auth');
const {putService, getService} = require('../util/service');
const {map, T} = require('../prelude');

// load :: a -> (a -> Future b c) -> Middleware b c
const load = B(B(Middleware.lift), T);
const load = map(map(Middleware.lift), T);

module.exports = App.do(function*(next) {
const {tokenLife, refreshLife} = yield getService('config').chain(load('security'));
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/cache.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

const {B, T} = require('sanctuary-env');
const {readStream, read, writeStream, write} = require('../services/cache');
const {putService, getService} = require('../util/service');
const {App, Middleware} = require('momi');
const {map, T} = require('../prelude');

module.exports = App.do(function*(next) {
const config = yield getService('config').chain(B(Middleware.lift, T('cache')));
const config = yield getService('config').chain(map(Middleware.lift, T('cache')));
yield putService('cache', {
readStream: readStream(config),
read: read(config),
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/http.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
'use strict';

const http = require('http');
const {B, T} = require('sanctuary-env');
const {getService} = require('../util/service');
const log = require('../util/log');
const {Middleware, App} = require('momi');
const Future = require('fluture');
const {map, T} = require('../prelude');

const mountApp = (app, host, port) => Middleware.lift(Future.node(done => {
const conn = http.createServer(app).listen(port, host, err => done(err, conn));
}));

module.exports = App.do(function*(next) {

const config = yield getService('config').chain(B(Middleware.lift, T('server.http')));
const config = yield getService('config').chain(map(Middleware.lift, T('server.http')));

if(!config.enabled) {
log.verbose('HTTP server is not enabled');
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/https.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

const https = require('https');
const {B, T} = require('sanctuary-env');
const {getService} = require('../util/service');
const log = require('../util/log');
const {Middleware, App} = require('momi');
const Future = require('fluture');
const fs = require('fs');
const {map, T} = require('../prelude');

const readFile = x => Future.node(done => fs.readFile(x, done));

Expand All @@ -16,7 +16,7 @@ const mountApp = (app, key, cert, host, port) => Middleware.lift(Future.node(don

module.exports = App.do(function*(next) {

const config = yield getService('config').chain(B(Middleware.lift, T('server.https')));
const config = yield getService('config').chain(map(Middleware.lift, T('server.https')));

if(!config.enabled) {
log.verbose('HTTPS server is not enabled');
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/service.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const {Middleware} = require('momi');
const {K} = require('sanctuary-env');
const {K} = require('../prelude');

// Services :: StrMap String Any
const Services = () => ({});
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/sigint.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

const {K} = require('sanctuary-env');
const {Middleware} = require('momi');
const Future = require('fluture');
const log = require('../util/log');
const {K} = require('../prelude');

//This bootstrapper keeps the process alive until a SIGINT is received.
// default :: a -> Middleware a b ()
Expand Down
4 changes: 2 additions & 2 deletions src/bootstrap/token.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
'use strict';

const {B, T} = require('sanctuary-env');
const {encode, decode} = require('../services/token');
const {App, Middleware} = require('momi');
const {putService, getService} = require('../util/service');
const {map, T} = require('../prelude');

module.exports = App.do(function*(next) {
const secret = yield getService('config').chain(B(Middleware.lift, T('security.secret')));
const secret = yield getService('config').chain(map(Middleware.lift, T('security.secret')));
yield putService('token', {
encode: encode(secret),
decode: decode(secret)
Expand Down
2 changes: 1 addition & 1 deletion src/bootstrap/users.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';

const {K, Just} = require('sanctuary-env');
const Future = require('fluture');
const bcrypt = require('bcrypt');
const {User} = require('../domain/models');
const {putService} = require('../util/service');
const {K, Just} = require('../prelude');

const getUserByUsername = username =>
Future.node(done => bcrypt.hash('password123', 10, done))
Expand Down
3 changes: 1 addition & 2 deletions src/domain/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ const def = require('../util/typedef');
const tcomb = require('tcomb');
const {union} = require('tcomb');
const {Readable} = require('stream');
const {test} = require('sanctuary-env');
const {complement: not} = require('ramda');
const {test, complement: not} = require('../prelude');

//Extend tcomb default types.
const T = Object.create(tcomb);
Expand Down
Loading