Skip to content

Commit 30df1bc

Browse files
author
Eran Hammer
committed
Auth views. Closes hapijs#2234
1 parent d1fc557 commit 30df1bc

File tree

3 files changed

+98
-37
lines changed

3 files changed

+98
-37
lines changed

lib/auth.js

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,20 @@ internals.Auth.prototype.strategy = function (name, scheme /*, mode, options */)
4343
Hoek.assert(scheme, 'Authentication strategy', name, 'missing scheme');
4444
Hoek.assert(this._schemes[scheme], 'Authentication strategy', name, 'uses unknown scheme:', scheme);
4545

46-
var strategy = this._schemes[scheme](this.connection.server._clone([this.connection]), options);
46+
var server = this.connection.server._clone([this.connection], '');
47+
var strategy = this._schemes[scheme](server, options);
48+
4749
Hoek.assert(strategy.authenticate, 'Invalid scheme:', name, 'missing authenticate() method');
4850
Hoek.assert(typeof strategy.authenticate === 'function', 'Invalid scheme:', name, 'invalid authenticate() method');
4951
Hoek.assert(!strategy.payload || typeof strategy.payload === 'function', 'Invalid scheme:', name, 'invalid payload() method');
5052
Hoek.assert(!strategy.response || typeof strategy.response === 'function', 'Invalid scheme:', name, 'invalid response() method');
5153
strategy.options = strategy.options || {};
5254
Hoek.assert(strategy.payload || !strategy.options.payload, 'Cannot require payload validation without a payload method');
53-
this._strategies[name] = strategy;
55+
56+
this._strategies[name] = {
57+
methods: strategy,
58+
realm: server.realm
59+
};
5460

5561
if (mode) {
5662
this.default({ strategies: [name], mode: mode === true ? 'required' : mode });
@@ -93,8 +99,8 @@ internals.Auth.prototype.test = function (name, request, next) {
9399
return next(response, data && data.credentials);
94100
};
95101

96-
var reply = request.server._replier.interface(request, null, transfer);
97-
strategy.authenticate(request, reply);
102+
var reply = request.server._replier.interface(request, strategy.realm, transfer);
103+
strategy.methods.authenticate(request, reply);
98104
};
99105

100106

@@ -131,9 +137,9 @@ internals.Auth.prototype._setupRoute = function (options, path) {
131137

132138
var strategy = self._strategies[name];
133139
Hoek.assert(strategy, 'Unknown authentication strategy:', name, 'in path:', path);
134-
Hoek.assert(strategy.payload || options.payload !== 'required', 'Payload validation can only be required when all strategies support it in path:', path);
135-
hasAuthenticatePayload = hasAuthenticatePayload || strategy.payload;
136-
Hoek.assert(!strategy.options.payload || options.payload === undefined || options.payload === 'required', 'Cannot set authentication payload to', options.payload, 'when a strategy requires payload validation', path);
140+
Hoek.assert(strategy.methods.payload || options.payload !== 'required', 'Payload validation can only be required when all strategies support it in path:', path);
141+
hasAuthenticatePayload = hasAuthenticatePayload || strategy.methods.payload;
142+
Hoek.assert(!strategy.methods.options.payload || options.payload === undefined || options.payload === 'required', 'Cannot set authentication payload to', options.payload, 'when a strategy requires payload validation', path);
137143
});
138144

139145
Hoek.assert(!options.payload || hasAuthenticatePayload, 'Payload authentication requires at least one strategy with payload support in path:', path);
@@ -193,24 +199,25 @@ internals.Auth.prototype._authenticate = function (request, next) {
193199
return next(err);
194200
}
195201

196-
var strategy = config.strategies[strategyPos];
202+
var name = config.strategies[strategyPos];
197203
++strategyPos;
198204

199-
request._protect.run('auth:request:' + strategy, validate, function (exit) {
205+
request._protect.run('auth:request:' + name, validate, function (exit) {
200206

201207
var transfer = function (response, data) {
202208

203-
exit(response, strategy, data);
209+
exit(response, name, data);
204210
};
205211

206-
var reply = request.server._replier.interface(request, null, transfer);
207-
self._strategies[strategy].authenticate(request, reply);
212+
var strategy = self._strategies[name];
213+
var reply = request.server._replier.interface(request, strategy.realm, transfer);
214+
strategy.methods.authenticate(request, reply);
208215
});
209216
};
210217

211-
var validate = function (err, strategy, result) { // err can be Boom, Error, or a valid response object
218+
var validate = function (err, name, result) { // err can be Boom, Error, or a valid response object
212219

213-
if (!strategy) {
220+
if (!name) {
214221
return next(err);
215222
}
216223

@@ -226,27 +233,27 @@ internals.Auth.prototype._authenticate = function (request, next) {
226233

227234
if (err) {
228235
if (err instanceof Error === false) {
229-
request._log(['auth', 'unauthenticated', 'response', strategy], err.statusCode);
236+
request._log(['auth', 'unauthenticated', 'response', name], err.statusCode);
230237
return next(err);
231238
}
232239

233-
request._log(['auth', 'unauthenticated', 'error', strategy], err);
240+
request._log(['auth', 'unauthenticated', 'error', name], err);
234241

235242
if (err.isMissing) {
236243

237-
// Try next strategy
244+
// Try next name
238245

239246
authErrors.push(err.output.headers['WWW-Authenticate']);
240247
return authenticate();
241248
}
242249

243250
if (config.mode === 'try') {
244251
request.auth.isAuthenticated = false;
245-
request.auth.strategy = strategy;
252+
request.auth.strategy = name;
246253
request.auth.credentials = result.credentials;
247254
request.auth.artifacts = result.artifacts;
248255
request.auth.error = err;
249-
request._log(['auth', 'unauthenticated', 'try', strategy], err);
256+
request._log(['auth', 'unauthenticated', 'try', name], err);
250257
return next();
251258
}
252259

@@ -256,7 +263,7 @@ internals.Auth.prototype._authenticate = function (request, next) {
256263
// Authenticated
257264

258265
var credentials = result.credentials;
259-
request.auth.strategy = strategy;
266+
request.auth.strategy = name;
260267
request.auth.credentials = credentials;
261268
request.auth.artifacts = result.artifacts;
262269

@@ -268,7 +275,7 @@ internals.Auth.prototype._authenticate = function (request, next) {
268275
(typeof credentials.scope === 'string' ? config.scope !== credentials.scope : credentials.scope.indexOf(config.scope) === -1) :
269276
(typeof credentials.scope === 'string' ? config.scope.indexOf(credentials.scope) === -1 : !Hoek.intersect(config.scope, credentials.scope).length))) {
270277

271-
request._log(['auth', 'scope', 'error', strategy], { got: credentials.scope, need: config.scope });
278+
request._log(['auth', 'scope', 'error', name], { got: credentials.scope, need: config.scope });
272279
return next(Boom.forbidden('Insufficient scope, expected any of: ' + config.scope));
273280
}
274281
}
@@ -280,7 +287,7 @@ internals.Auth.prototype._authenticate = function (request, next) {
280287
// Entity: 'any'
281288

282289
if (entity === 'any') {
283-
request._log(['auth', strategy]);
290+
request._log(['auth', name]);
284291
request.auth.isAuthenticated = true;
285292
return next();
286293
}
@@ -289,23 +296,23 @@ internals.Auth.prototype._authenticate = function (request, next) {
289296

290297
if (entity === 'user') {
291298
if (!credentials.user) {
292-
request._log(['auth', 'entity', 'user', 'error', strategy]);
299+
request._log(['auth', 'entity', 'user', 'error', name]);
293300
return next(Boom.forbidden('Application credentials cannot be used on a user endpoint'));
294301
}
295302

296-
request._log(['auth', strategy]);
303+
request._log(['auth', name]);
297304
request.auth.isAuthenticated = true;
298305
return next();
299306
}
300307

301308
// Entity: 'app'
302309

303310
if (credentials.user) {
304-
request._log(['auth', 'entity', 'app', 'error', strategy]);
311+
request._log(['auth', 'entity', 'app', 'error', name]);
305312
return next(Boom.forbidden('User credentials cannot be used on an application endpoint'));
306313
}
307314

308-
request._log(['auth', strategy]);
315+
request._log(['auth', name]);
309316
request.auth.isAuthenticated = true;
310317
return next();
311318
};
@@ -333,12 +340,12 @@ internals.Auth.payload = function (request, next) {
333340
var auth = request.connection.auth;
334341
var strategy = auth._strategies[request.auth.strategy];
335342

336-
if (!strategy.payload) {
343+
if (!strategy.methods.payload) {
337344
return next();
338345
}
339346

340347
var config = auth._routeConfig(request);
341-
var setting = config.payload || (strategy.options.payload ? 'required' : false);
348+
var setting = config.payload || (strategy.methods.options.payload ? 'required' : false);
342349
if (!setting) {
343350
return next();
344351
}
@@ -357,8 +364,8 @@ internals.Auth.payload = function (request, next) {
357364

358365
request._protect.run('auth:payload:' + request.auth.strategy, finalize, function (exit) {
359366

360-
var reply = request.server._replier.interface(request, null, exit);
361-
strategy.payload(request, reply);
367+
var reply = request.server._replier.interface(request, strategy.realm, exit);
368+
strategy.methods.payload(request, reply);
362369
});
363370
};
364371

@@ -375,13 +382,13 @@ internals.Auth.response = function (request, next) {
375382
}
376383

377384
var strategy = auth._strategies[request.auth.strategy];
378-
if (!strategy.response) {
385+
if (!strategy.methods.response) {
379386
return next();
380387
}
381388

382389
request._protect.run('auth:response:' + request.auth.strategy, next, function (exit) {
383390

384-
var reply = request.server._replier.interface(request, null, exit);
385-
strategy.response(request, reply);
391+
var reply = request.server._replier.interface(request, strategy.realm, exit);
392+
strategy.methods.response(request, reply);
386393
});
387394
};

lib/plugin.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,15 @@ internals.Plugin.prototype._select = function (labels, plugin, options) {
132132
}
133133
}
134134

135-
return new internals.Plugin(this.root, connections, plugin || this.realm, options);
135+
var env = (plugin !== undefined ? plugin : this.realm); // Allow empty string
136+
return new internals.Plugin(this.root, connections, env, options);
136137
};
137138

138139

139-
internals.Plugin.prototype._clone = function (connections) {
140+
internals.Plugin.prototype._clone = function (connections, plugin) {
140141

141-
return new internals.Plugin(this.root, connections, this.realm);
142+
var env = (plugin !== undefined ? plugin : this.realm); // Allow empty string
143+
return new internals.Plugin(this.root, connections, env);
142144
};
143145

144146

test/auth.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Load modules
22

3+
var Path = require('path');
34
var Boom = require('boom');
45
var Code = require('code');
6+
var Handlebars = require('handlebars');
57
var Hapi = require('..');
68
var Hoek = require('hoek');
79
var Lab = require('lab');
@@ -104,6 +106,56 @@ describe('authentication', function () {
104106
});
105107
});
106108
});
109+
110+
it('uses views', function (done) {
111+
112+
var implementation = function (server, options) {
113+
114+
server.views({
115+
engines: { 'html': Handlebars },
116+
relativeTo: Path.join(__dirname, '/templates/plugin')
117+
});
118+
119+
var handler = function (request, reply) {
120+
121+
return reply.view('test', { message: 'steve' });
122+
};
123+
124+
server.route({ method: 'GET', path: '/view', handler: handler, config: { auth: false } });
125+
126+
return {
127+
authenticate: function (request, reply) {
128+
129+
return reply.view('test', { message: 'xyz' });
130+
}
131+
};
132+
};
133+
134+
var server = new Hapi.Server();
135+
server.connection();
136+
137+
server.views({
138+
engines: { 'html': Handlebars },
139+
relativeTo: Path.join(__dirname, '/no/such/directory')
140+
});
141+
142+
server.auth.scheme('custom', implementation);
143+
server.auth.strategy('default', 'custom', true);
144+
145+
server.route({ method: 'GET', path: '/', handler: function (request, reply) { return reply(); } });
146+
147+
server.inject('/view', function (res) {
148+
149+
expect(res.result).to.equal('<h1>steve</h1>');
150+
151+
server.inject('/', function (res) {
152+
153+
expect(res.statusCode).to.equal(200);
154+
expect(res.result).to.equal('<h1>xyz</h1>');
155+
done();
156+
});
157+
});
158+
});
107159
});
108160

109161
describe('default()', function () {
@@ -677,7 +729,7 @@ describe('authentication', function () {
677729
var server = new Hapi.Server();
678730
server.connection();
679731
server.auth.scheme('custom', internals.implementation);
680-
server.auth.strategy('default', 'custom', { users: { steve: { } } });
732+
server.auth.strategy('default', 'custom', { users: { steve: {} } });
681733
server.auth.default({
682734
strategy: 'default',
683735
scope: 'one'

0 commit comments

Comments
 (0)