Skip to content

Commit

Permalink
Auth reply interface. Closes hapijs#1285. Closes hapijs#1284
Browse files Browse the repository at this point in the history
  • Loading branch information
Eran Hammer committed Jan 2, 2014
1 parent 9cf4a71 commit 40198f6
Show file tree
Hide file tree
Showing 12 changed files with 69 additions and 143 deletions.
40 changes: 10 additions & 30 deletions docs/Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
- [`request.log(tags, [data, [timestamp]])`](#requestlogtags-data-timestamp)
- [`request.getLog([tags])`](#requestgetlogtags)
- [`request.tail([name])`](#requesttailname)
- [`request.response()`](#requestresponse)
- [`Hapi.response`](#hapiresponse)
- [Flow control](#flow-control)
- [Response types](#response-types)
Expand Down Expand Up @@ -1443,16 +1442,14 @@ Each incoming request passes through a pre-defined set of steps, along with opti
- Route prerequisites
- Route handler
- **`'onPostHandler'`** extension point
- the `request` object passed to the extension function is decorated with the `request.response()` method which returns the response object.
The response object may be modified. To return a different response (for example, replace an error with an HTML response), return the new
response via `next(response)`.
- The response contained in `request.response` may be modified. To return a different response type (for example, replace an error
with an HTML response), return a new response via `next(response)`.
- Validate response payload
- **`'onPreResponse'`** extension point
- always called.
- the `request` object passed to the extension function is decorated with the `request.response()` method which returns the response object.
The response object may be modified. To return a different response (for example, replace an error with an HTML response), return a new
response via `next(response)`. Note that any errors generated after `next(response)` is called will not be passed back to the `'onPreResponse'`
extention method to prevent an infinite loop.
- The response contained in `request.response` may be modified. To return a different response type (for example, replace an error
with an HTML response), return a new response via `next(response)`. Note that any errors generated after `next(response)` is called
will not be passed back to the `'onPreResponse'` extention method to prevent an infinite loop.
- Send response (may emit `'internalError'` event)
- Emits `'response'` event
- Wait for tails
Expand Down Expand Up @@ -1675,8 +1672,10 @@ Each request object has the following properties:
- `plugins` - plugin-specific state. Provides a place to store and pass request-level plugin data. The `plugins` is an object where each
key is a plugin name and the value is the state.
- `pre` - an object where each key is the name assigned by a [route prerequisites](#route-prerequisites) function. The values are the raw values
provided to the continuation function as argument. For the wrapped response object, use `request.responses`.
- `responses` - same as `request.pre` but represented as the response object created by the pre method.
provided to the continuation function as argument. For the wrapped response object, use `responses`.
- `response` - the response object when set. The object can be modified but must not be assigned another object. To replace the response
with another from within an extension point, use `next(response)` to override with a different response.
- `responses` - same as `pre` but represented as the response object created by the pre method.
- `query` - an object containing the query parameters.
- `raw` - an object containing the Node HTTP server objects. **Direct interaction with these raw objects is not recommended.**
- `req` - the `request` object.
Expand Down Expand Up @@ -1815,25 +1814,6 @@ server.on('tail', function (request) {
});
```

#### `request.response()`

_Available after the handler method concludes and immediately after the `'onPreResponse'` extension point methods._

Returns the response object. The object can be modified but cannot be assigned another object. To replace the response with another
from within an extension point, use `next(response)` to return a different response.

```javascript
var Hapi = require('hapi');
var server = new Hapi.Server();

server.ext('onPostHandler', function (request, next) {

var response = request.response();
delete response.source._id; // Remove internal key
next();
});
```

## `Hapi.response`

### Flow control
Expand Down Expand Up @@ -2068,7 +2048,7 @@ var server = new Hapi.Server({ views: { engines: { html: 'handlebars' } } });

server.ext('onPreResponse', function (request, reply) {

var response = request.response();
var response = request.response;
if (!response.isBoom) {
return next();
}
Expand Down
4 changes: 2 additions & 2 deletions examples/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ internals.onPreResponse = function (request, next) {

// Demonstrates how to hide error messages from the client

if (request.response().isBoom) {
var error = request.response();
if (request.response.isBoom) {
var error = request.response;
error.response.payload.message = 'Censored Error';
}

Expand Down
6 changes: 3 additions & 3 deletions lib/auth/cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ internals.Scheme.prototype.authenticate = function (request, reply) {
!isValid) {

if (self.settings.clearInvalid) {
request._clearState(self.settings.cookie);
reply.unstate(self.settings.cookie);
}

return unauthenticated(Boom.unauthorized('Invalid cookie'), { credentials: credentials, log: (err ? { data: err } : 'Failed validation') });
}

if (credentials) {
request._setState(self.settings.cookie, credentials);
reply.state(self.settings.cookie, credentials);
}

return reply(null, { credentials: credentials || session });
Expand All @@ -98,7 +98,7 @@ internals.Scheme.prototype.authenticate = function (request, reply) {
uri += self.settings.appendNext + '=' + encodeURIComponent(request.url.path);
}

return reply(request.generateResponse().redirect(uri), result);
return reply('You are being redirected...', result).redirect(uri);
};

validate();
Expand Down
37 changes: 27 additions & 10 deletions lib/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var Hawk = require('./hawk');
var Bewit = require('./bewit');
var Basic = require('./basic');
var Cookie = require('./cookie');
var Handler = require('../handler');
var Utils = require('../utils');


Expand Down Expand Up @@ -200,23 +201,39 @@ internals.Auth.prototype.authenticate = function (request, next) {
return next(Boom.unauthorized('Missing authentication', authErrors));
}

// Generate reply interface

var savedResults = undefined;
var transfer = function (err) {

validate(err, savedResults);
};

var root = function (err, result) {

savedResults = result;
return (err ? reply._root(err) : validate(err, result));
};

var reply = Handler.replyInterface(request, transfer, root);

var strategy = self._strategies[config.strategies[strategyPos++]]; // Increments counter after fetching current strategy
return strategy.authenticate(request, validate);
return strategy.authenticate(request, reply);
};

var validate = function (err, results) {
var validate = function (err, result) {

results = results || {};
result = result || {};

// Unauthenticated

if (!err && !results.credentials) {
if (!err && !result.credentials) {
return next(Boom.badImplementation('Authentication response missing both error and credentials'));
}

if (err) {
if (results.log) {
request.log(['hapi', 'auth', 'error', config.strategies[strategyPos]].concat(results.log.tags), results.log.data);
if (result.log) {
request.log(['hapi', 'auth', 'error', config.strategies[strategyPos]].concat(result.log.tags), result.log.data);
}
else {
request.log(['hapi', 'auth', 'error', 'unauthenticated'], err);
Expand All @@ -228,8 +245,8 @@ internals.Auth.prototype.authenticate = function (request, next) {

if (config.mode === 'try') {
request.auth.isAuthenticated = false;
request.auth.credentials = results.credentials;
request.auth.artifacts = results.artifacts;
request.auth.credentials = result.credentials;
request.auth.artifacts = result.artifacts;
request.log(['hapi', 'auth', 'error', 'unauthenticated', 'try'], err);
return next();
}
Expand All @@ -248,9 +265,9 @@ internals.Auth.prototype.authenticate = function (request, next) {

// Authenticated

var credentials = results.credentials;
var credentials = result.credentials;
request.auth.credentials = credentials;
request.auth.artifacts = results.artifacts;
request.auth.artifacts = result.artifacts;
request.auth._strategy = self._strategies[config.strategies[strategyPos - 1]];

// Check scope
Expand Down
8 changes: 4 additions & 4 deletions lib/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ internals.handler = function (request, callback) {

// Decorate request

var reply = internals.replyInterface(request, finalize);
var reply = exports.replyInterface(request, finalize);
var bind = (request.route.bind || request._route.env.bind);

// Execute handler
Expand All @@ -98,7 +98,7 @@ internals.handler = function (request, callback) {
};


internals.replyInterface = function (request, finalize, base) {
exports.replyInterface = function (request, finalize, base) {

finalize = Utils.once(finalize);

Expand Down Expand Up @@ -327,7 +327,7 @@ internals.pre = function (pre) {

// Setup environment

var reply = internals.replyInterface(request, exit);
var reply = exports.replyInterface(request, exit);
var bind = (request.route.bind || request._route.env.bind);

// Execute handler
Expand Down Expand Up @@ -361,7 +361,7 @@ exports.invoke = function (request, event, callback) {

finalize.env = ext.env;

var reply = internals.replyInterface(request, exit, finalize);
var reply = exports.replyInterface(request, exit, finalize);
var bind = (ext.bind || (ext.env && ext.env.bind));

enter(function () {
Expand Down
30 changes: 7 additions & 23 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ exports = module.exports = internals.Request = function (server, req, res, optio
this.payload = null;
this.state = null;
this.jsonp = null;
this.response = null; // this.response()
this.response = null;

// Semi-public members

Expand All @@ -96,16 +96,12 @@ exports = module.exports = internals.Request = function (server, req, res, optio

// Private members

this._states = {}; // Appended to response states when setting response headers
this._states = {};
this._logger = [];

this._response = null;
this._isReplied = false;

this._tails = {}; // tail id -> name (tracks pending tails)
this._tailIds = 0; // Used to generate a unique tail id
this._isWagging = false; // true when request completed and only waiting on tails to complete

this._paramsArray = []; // Array of path parameters in path order

// Set socket timeout
Expand Down Expand Up @@ -326,10 +322,10 @@ internals.Request.prototype._reply = function (exit) {

var process = function () {

if (self._response && // Can be null if response coming from exit
self._response.closed) {
if (self.response && // Can be null if response coming from exit
self.response.closed) {

if (self._response.end) {
if (self.response.end) {
self.raw.res.end(); // End the response in case it wasn't already closed
}
return finalize();
Expand All @@ -341,8 +337,6 @@ internals.Request.prototype._reply = function (exit) {

self.server._ext.invoke(self, 'onPreResponse', function (err) {

self.response = undefined;

if (err) { // err can be valid response or error
self._setResponse(Response.wrap(err, self));
}
Expand All @@ -353,7 +347,7 @@ internals.Request.prototype._reply = function (exit) {

var finalize = function () {

self.server._dtrace.report('request.finalize', self._response);
self.server._dtrace.report('request.finalize', self.response);
self.server.emit('response', self);

self._isWagging = true;
Expand Down Expand Up @@ -399,11 +393,7 @@ internals.Request.prototype._setResponse = function (response) {

var self = this;

this._response = response;
this.response = this.response || function () {

return self._response;
};
this.response = response;

if (response.isBoom &&
response.response.statusCode === 500) {
Expand Down Expand Up @@ -474,9 +464,3 @@ internals.Request.prototype._clearState = function (name) {

this._states[name] = state;
};


internals.Request.prototype.generateResponse = function (result) {

return Response.wrap(result, this);
};
2 changes: 1 addition & 1 deletion lib/response/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ exports.send = function (request, callback) {

var prepare = function () {

var response = request._response;
var response = request.response;
if (response.isBoom) {
return fail(response);
}
Expand Down
8 changes: 4 additions & 4 deletions lib/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,17 @@ exports.response = function (request, next) {
}
}

if (request._response.isBoom) {
if (request.response.isBoom) {
return next();
}

if (request._response.variety !== 'plain' ||
typeof request._response.source !== 'object') {
if (request.response.variety !== 'plain' ||
typeof request.response.source !== 'object') {

return next(Boom.badImplementation('Cannot validate non-object response'));
}

var error = Joi.validate(request._response.source, request.route.response.schema || {}, request.server.settings.validation);
var error = Joi.validate(request.response.source, request.route.response.schema || {}, request.server.settings.validation);
if (!error) {
return next();
}
Expand Down
6 changes: 3 additions & 3 deletions test/integration/ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('Ext', function () {
var server = new Hapi.Server();
server.ext('onPreResponse', function (request, next) {

return next(typeof request.response().source === 'string' ? Hapi.error.badRequest('boom') : undefined);
return next(typeof request.response.source === 'string' ? Hapi.error.badRequest('boom') : undefined);
});

server.route({ method: 'GET', path: '/text', handler: function (request, reply) { reply('ok'); } });
Expand All @@ -109,7 +109,7 @@ describe('Ext', function () {
var server = new Hapi.Server();
server.ext('onPreResponse', function (request, next) {

return next(null, request.response().response.statusCode);
return next(null, request.response.response.statusCode);
});

server.inject({ method: 'GET', url: '/missing' }, function (res) {
Expand All @@ -126,7 +126,7 @@ describe('Ext', function () {

server.ext('onPreResponse', function (request, next) {

var response = request.response();
var response = request.response;
return next({ isBoom: response.isBoom });
});

Expand Down
2 changes: 1 addition & 1 deletion test/integration/pack/--context/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ exports.register = function (plugin, options, next) {

plugin.ext('onPreResponse', function (request, next) {

next(request.response().source + this.suffix);
next(request.response.source + this.suffix);
});

plugin.bind(bind); // Call last to test late binding
Expand Down
Loading

0 comments on commit 40198f6

Please sign in to comment.