feat(email): fetch email from auth-server /account/profile

Closes #165
This commit is contained in:
Sean McArthur 2015-11-09 12:29:32 -08:00
Родитель b4413aefe8
Коммит cc70645763
8 изменённых файлов: 463 добавлений и 993 удалений

Просмотреть файл

@ -14,6 +14,14 @@ const conf = convict({
default: 1
}
},
authServer: {
url: {
doc: 'URL of fxa-auth-server',
env: 'AUTH_SERVER_URL',
format: 'url',
default: 'http://127.0.0.1:9000/v1'
}
},
db: {
driver: {
env: 'DB',

Просмотреть файл

@ -138,5 +138,16 @@ AppError.oauthError = function oauthError(err) {
});
};
AppError.authError = function authError(err) {
return new AppError({
code: 503,
error: 'Service Unavailable',
errno: 105,
message: 'Auth server error'
}, {
cause: err
});
};
module.exports = AppError;

Просмотреть файл

@ -5,6 +5,11 @@
const Joi = require('joi');
const AppError = require('../error');
const config = require('../config');
const logger = require('../logging')('routes.email');
const request = require('../request');
const AUTH_SERVER_URL = config.get('authServer.url') + '/account/profile';
module.exports = {
auth: {
@ -17,14 +22,30 @@ module.exports = {
}
},
handler: function email(req, reply) {
var email = req.auth.credentials.email;
if (email) {
request.get(AUTH_SERVER_URL, {
headers: {
Authorization: 'Bearer ' + req.auth.token
},
json: true
}, function(err, res, body) {
if (err) {
logger.error('request.auth_server.network', err);
return reply(new AppError.authError('network error'));
}
if (res.statusCode >= 400) {
logger.error('request.auth_server.fail', { code: res.statusCode });
return reply(new AppError.authError('auth server error'));
}
if (!body || !body.email) {
return reply(
new AppError.authError('email field missing from auth response')
);
}
reply({
email: email
email: body.email
});
} else {
reply(new AppError.oauthError('email field missing from oauth response'));
}
});
}
};

Просмотреть файл

@ -54,11 +54,13 @@ exports.create = function createServer() {
request.post({
url: url,
json: {
token: token
token: token,
email: false // disables email fetching of oauth server
}
}, function(err, resp, body) {
if (err || resp.statusCode >= 500) {
err = err || resp.statusMessage || 'unknown';
logger.error('oauth.error', err);
return reply(AppError.oauthError(err));
}
if (body.code >= 400) {
@ -67,7 +69,8 @@ exports.create = function createServer() {
}
logger.debug('auth.valid', body);
reply(null, {
credentials: body
credentials: body,
token: token
});
});
}

1323
npm-shrinkwrap.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -42,7 +42,7 @@
"load-grunt-tasks": "^1.0.0",
"mkdirp": "^0.5.0",
"mocha-text-cov": "^0.1.0",
"nock": "^0.48.0",
"nock": "^2.17.0",
"pngparse": "2.0.1",
"rimraf": "^2.4.3",
"through": "^2.3.4",

Просмотреть файл

@ -61,6 +61,7 @@ describe('/profile', function() {
it('should return all of a profile', function() {
mock.tokenGood();
mock.email('user@example.domain');
return Server.api.get({
url: '/profile',
headers: {
@ -74,12 +75,12 @@ describe('/profile', function() {
});
});
it('should handle oauth errors', function() {
it('should handle auth server errors', function() {
mock.token({
user: USERID,
scope: ['profile:write']
// intentionally left off email
});
mock.emailFailure();
mock.log('server', function(rec) {
return rec.levelname === 'ERROR'
@ -98,6 +99,11 @@ describe('/profile', function() {
&& rec.args[1].path === '/v1/profile';
});
mock.log('routes.email', function(rec) {
return rec.levelname === 'ERROR'
&& rec.args[0] === 'request.auth_server.fail';
});
return Server.api.get({
url: '/profile',
headers: {
@ -105,7 +111,7 @@ describe('/profile', function() {
}
}).then(function(res) {
assert.equal(res.statusCode, 503);
assert.equal(res.result.errno, 104);
assert.equal(res.result.errno, 105);
});
});
@ -132,9 +138,9 @@ describe('/profile', function() {
it('should return an avatar if selected', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile']
});
mock.email('user@example.domain');
var aid = avatarId();
var PROVIDER = 'gravatar';
return db.addAvatar(aid, user, GRAVATAR, PROVIDER, true)
@ -154,9 +160,9 @@ describe('/profile', function() {
it('should return a display name if set', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile']
});
mock.email('user@example.domain');
return db.setDisplayName(user, 'Spock')
.then(function() {
return Server.api.get({
@ -174,9 +180,9 @@ describe('/profile', function() {
it('should return filtered profile if smaller scope', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:email']
});
mock.email('user@example.domain');
return Server.api.get({
url: '/profile',
headers: {
@ -192,7 +198,6 @@ describe('/profile', function() {
it('should require a profile:* scope', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['some:other:scope']
});
return Server.api.get({
@ -208,9 +213,9 @@ describe('/profile', function() {
it('should include an etag in the http response', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile']
});
mock.email('user@example.domain');
return db.setDisplayName(user, 'Spock')
.then(function() {
return Server.api.get({
@ -233,6 +238,7 @@ describe('/email', function() {
it('should return an email', function() {
mock.tokenGood();
mock.email('user@example.domain');
return Server.api.get({
url: '/email',
headers: {
@ -247,7 +253,6 @@ describe('/email', function() {
it('should NOT return email if wrong scope', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:uid']
});
return Server.api.get({
@ -280,7 +285,6 @@ describe('/uid', function() {
it('should NOT return a uid if wrong scope', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:email']
});
return Server.api.get({
@ -317,7 +321,6 @@ describe('/avatar', function() {
it('should return selected avatar', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar']
});
return Server.api.get({
@ -334,7 +337,6 @@ describe('/avatar', function() {
it('should include an etag in the http response', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar']
});
return Server.api.get({
@ -355,7 +357,6 @@ describe('/avatar', function() {
it('should log an avatar.get activity event', function(done) {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar']
});
@ -381,7 +382,6 @@ describe('/avatar', function() {
it('should post a new avatar url', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
return Server.api.post({
@ -401,7 +401,6 @@ describe('/avatar', function() {
it('should log the avatar.post activity event', function(done) {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
@ -429,7 +428,6 @@ describe('/avatar', function() {
it('should check url matches a provider', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
return Server.api.post({
@ -455,7 +453,6 @@ describe('/avatar', function() {
this.timeout(3000);
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
mock.image(imageData.length);
@ -486,7 +483,6 @@ describe('/avatar', function() {
it('should gracefully handle and report upload failures', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
mock.workerFailure('post', imageData.length);
@ -531,7 +527,6 @@ describe('/avatar', function() {
it('should fail if not owned by user', function() {
mock.token({
user: uid(),
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
return Server.api.delete({
@ -547,7 +542,6 @@ describe('/avatar', function() {
it('should remove avatar from user', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
return Server.api.delete({
@ -570,7 +564,6 @@ describe('/avatar', function() {
before(function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
mock.image(imageData.length);
@ -591,7 +584,6 @@ describe('/avatar', function() {
it('should remove avatar from db and s3', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
mock.deleteImage();
@ -642,7 +634,6 @@ describe('/avatars', function() {
it('should return a list of avatars', function() {
mock.token({
user: user,
email: 'user@example.domain',
scope: ['profile:avatar:write']
});
return Server.api.get({
@ -675,7 +666,6 @@ describe('/display_name', function() {
it('should return a displayName', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name']
});
@ -697,7 +687,6 @@ describe('/display_name', function() {
var userId = uid();
mock.token({
user: userId,
email: 'user@example.domain',
scope: ['profile:display_name']
});
@ -715,7 +704,6 @@ describe('/display_name', function() {
it('should NOT return a display_name if wrong scope', function() {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:email']
});
return Server.api.get({
@ -734,7 +722,6 @@ describe('/display_name', function() {
var NAME = 'Spock';
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name:write']
});
return Server.api.post({
@ -757,7 +744,6 @@ describe('/display_name', function() {
var NAME = 'Spock';
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name:write']
});
return Server.api.post({
@ -772,7 +758,6 @@ describe('/display_name', function() {
assert.equal(res.statusCode, 200);
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name:write']
});
return Server.api.post({
@ -788,7 +773,6 @@ describe('/display_name', function() {
assert.equal(res.statusCode, 200);
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name']
});
return Server.api.get({
@ -811,7 +795,6 @@ describe('/display_name', function() {
return P.resolve(NAMES).each(function(NAME) {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name:write']
});
return Server.api.post({
@ -826,7 +809,6 @@ describe('/display_name', function() {
assert.equal(res.statusCode, 200);
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name']
});
return Server.api.get({
@ -858,7 +840,6 @@ describe('/display_name', function() {
return P.resolve(NAMES).each(function(NAME) {
mock.token({
user: USERID,
email: 'user@example.domain',
scope: ['profile:display_name:write']
});
return Server.api.post({

Просмотреть файл

@ -24,7 +24,6 @@ module.exports = function mock(options) {
const TOKEN_GOOD = JSON.stringify({
user: options.userid,
scope: ['profile'],
email: 'user@example.domain'
});
const MOCK_ID = new Array(33).join('f');
@ -118,14 +117,18 @@ module.exports = function mock(options) {
tokenGood: function tokenGood() {
var parts = url.parse(config.get('oauth.url'));
return nock(parts.protocol + '//' + parts.host)
.post(parts.path + '/verify')
.post(parts.path + '/verify', function(body) {
return body.email === false;
})
.reply(200, TOKEN_GOOD);
},
token: function token(tok) {
var parts = url.parse(config.get('oauth.url'));
return nock(parts.protocol + '//' + parts.host)
.post(parts.path + '/verify')
.post(parts.path + '/verify', function(body) {
return body.email === false;
})
.reply(200, JSON.stringify(tok));
},
@ -137,6 +140,22 @@ module.exports = function mock(options) {
},
email: function mockEmail(email) {
var parts = url.parse(config.get('authServer.url'));
return nock(parts.protocol + '//' + parts.host)
.get(parts.path + '/account/profile')
.reply(200, {
email: email
});
},
emailFailure: function mockEmailFailure() {
var parts = url.parse(config.get('authServer.url'));
return nock(parts.protocol + '//' + parts.host)
.get(parts.path + '/account/profile')
.reply(500);
},
workerFailure: function workerFailure(action, bytes) {
if (action !== 'post' && action !== 'delete') {
throw new Error('failure must be post or delete');