Skip to content

Commit 4f8baa6

Browse files
authored
feat: verify ban status on metadata request (#403)
1 parent 2056b80 commit 4f8baa6

11 files changed

Lines changed: 156 additions & 171 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
"apidoc": "^0.17.7",
8484
"apidoc-plugin-schema": "^0.1.8",
8585
"babel-eslint": "^10.0.1",
86-
"babel-plugin-istanbul": "^5.1.2",
86+
"babel-plugin-istanbul": "^5.1.3",
8787
"chai": "^4.2.0",
8888
"cheerio": "^1.0.0-rc.3",
8989
"codecov": "^3.3.0",

schemas/getMetadata.json

Lines changed: 55 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,59 @@
11
{
2-
"$id": "getMetadata",
3-
"type": "object",
4-
"required": [
5-
"username",
6-
"audience"
7-
],
8-
"properties": {
9-
"username": {
10-
"oneOf": [
11-
{
12-
"type": "string",
13-
"minLength": 1,
14-
"maxLength": 50
15-
},
16-
{
17-
"type": "array",
18-
"minItems": 1,
19-
"unique": true,
20-
"items": {
21-
"type": "string",
22-
"minLength": 1,
23-
"maxLength": 50
24-
}
25-
}
26-
]
27-
},
28-
"public": {
29-
"type": "boolean"
30-
},
31-
"audience": {
32-
"oneOf": [
33-
{
34-
"type": "string",
35-
"minLength": 1
36-
},
37-
{
38-
"type": "array",
39-
"items": {
40-
"type": "string",
41-
"minLength": 1
42-
},
43-
"minItems": 1
44-
}
45-
]
2+
"$id": "getMetadata",
3+
"type": "object",
4+
"required": [
5+
"username",
6+
"audience"
7+
],
8+
"properties": {
9+
"username": {
10+
"anyOf": [{
11+
"type": "string",
12+
"minLength": 1,
13+
"maxLength": 50
14+
}, {
15+
"type": "array",
16+
"minItems": 1,
17+
"unique": true,
18+
"items": {
19+
"type": "string",
20+
"minLength": 1,
21+
"maxLength": 50
22+
}
23+
}]
24+
},
25+
"public": {
26+
"type": "boolean"
27+
},
28+
"audience": {
29+
"anyOf": [{
30+
"type": "string",
31+
"minLength": 1
32+
}, {
33+
"type": "array",
34+
"items": {
35+
"type": "string",
36+
"minLength": 1
4637
},
47-
"fields": {
48-
"type": "object",
49-
"minProperties": 1,
50-
"patternProperties": {
51-
"^.+$": {
52-
"type": "array",
53-
"minItems": 1,
54-
"uniqueItems": true,
55-
"items": {
56-
"type": "string",
57-
"minLength": 1
58-
}
59-
}
60-
}
38+
"minItems": 1
39+
}]
40+
},
41+
"includingBanned": {
42+
"type": "boolean",
43+
"default": true
44+
},
45+
"fields": {
46+
"type": "object",
47+
"minProperties": 1,
48+
"additionalProperties": {
49+
"type": "array",
50+
"minItems": 1,
51+
"uniqueItems": true,
52+
"items": {
53+
"type": "string",
54+
"minLength": 1
6155
}
56+
}
6257
}
63-
}
58+
}
59+
}

src/actions/getMetadata.js

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ const Promise = require('bluebird');
33
const Errors = require('common-errors');
44
const noop = require('lodash/noop');
55
const identity = require('lodash/identity');
6-
const get = require('lodash/get');
6+
const get = require('../utils/get-value');
77
const getMetadata = require('../utils/getMetadata');
88
const { getUserId } = require('../utils/userData');
99
const { USERS_ALIAS_FIELD } = require('../constants');
@@ -17,19 +17,15 @@ const { isArray } = Array;
1717
*/
1818
function isPublic(audiences) {
1919
return (metadata, username) => {
20-
let notFound = true;
21-
2220
// iterate over passed audiences, generally we only retrieve one audience
2321
// so this check is cheap
24-
audiences.forEach((audience) => {
25-
if (notFound && get(metadata, [audience, USERS_ALIAS_FIELD]) === username) {
26-
notFound = false;
22+
for (const audience of Object.values(audiences)) {
23+
if (get(metadata, [audience, USERS_ALIAS_FIELD]) === username) {
24+
return;
2725
}
28-
});
29-
30-
if (notFound) {
31-
throw new Errors.HttpStatusError(404, 'username was not found');
3226
}
27+
28+
throw new Errors.HttpStatusError(404, 'username was not found');
3329
};
3430
}
3531

@@ -38,23 +34,23 @@ function isPublic(audiences) {
3834
* @param {String} username
3935
* @return {Promise}
4036
*/
41-
function retrieveMetadata(username) {
42-
return Promise
43-
.bind(this.service, username)
44-
.then(getUserId)
45-
.then(userId => [userId, this.audiences, this.fields])
46-
.spread(getMetadata)
47-
.tap(metadata => this.filter(metadata, username));
37+
async function retrieveMetadata(username) {
38+
const { service, audiences, fields, verifyBanned } = this;
39+
40+
const userId = await getUserId.call(service, username, verifyBanned);
41+
const metadata = await getMetadata.call(service, userId, audiences, fields);
42+
43+
this.filter(metadata, username);
44+
45+
return metadata;
4846
}
4947

5048
/**
5149
* If we request only 1 user - return unwrapped array
5250
* @param {Array} responses
5351
* @return {Array|Object}
5452
*/
55-
function extractResponse(responses) {
56-
return responses[0];
57-
}
53+
const extractResponse = responses => responses[0];
5854

5955
/**
6056
* @api {amqp} <prefix>.getMetadata Retrieve Public Data
@@ -73,11 +69,12 @@ function extractResponse(responses) {
7369
* @apiParam (Payload) {String[]} fields.* - fields to return from a passed audience
7470
*
7571
*/
76-
module.exports = function getMetadataAction(request) {
72+
async function getMetadataAction(request) {
7773
const {
7874
audience: _audience,
7975
username: _username,
8076
public: isPublicResponse,
77+
includingBanned,
8178
fields,
8279
} = request.params;
8380

@@ -92,13 +89,17 @@ module.exports = function getMetadataAction(request) {
9289
usernames,
9390
filter,
9491
fields,
92+
verifyBanned: includingBanned === false,
9593
service: this,
9694
};
9795

98-
return Promise
96+
const response = await Promise
9997
.bind(ctx, usernames)
100-
.map(retrieveMetadata)
101-
.then(unnest);
102-
};
98+
.map(retrieveMetadata);
99+
100+
return unnest(response);
101+
}
102+
103+
getMetadataAction.transports = [ActionTransport.amqp, ActionTransport.internal];
103104

104-
module.exports.transports = [ActionTransport.amqp, ActionTransport.internal];
105+
module.exports = getMetadataAction;

src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ module.exports = exports = {
6262
USERS_INVALID_TOKEN: new HttpStatusError(403, 'invalid token'),
6363
USERS_MALFORMED_TOKEN: new HttpStatusError(403, 'malformed token'),
6464
USER_ALREADY_ACTIVE: new HttpStatusError(417, 'this user is already active'),
65+
ErrorAccountLocked: new HttpStatusError(423, 'Account has been locked'),
6566
ErrorConflictUserExists: new HttpStatusError(409, 'user already exists'),
6667
ErrorConflictOrganizationExists: new HttpStatusError(409, 'organization already exists'),
6768
ErrorOrganizationNotFound: new HttpStatusError(404, 'organization not found'),

src/utils/isBanned.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
const Promise = require('bluebird');
2-
const Errors = require('common-errors');
3-
const { USERS_BANNED_FLAG } = require('../constants.js');
1+
const { USERS_BANNED_FLAG, ErrorAccountLocked } = require('../constants');
42

53
module.exports = function isBanned(data) {
64
if (String(data[USERS_BANNED_FLAG]) === 'true') {
7-
return Promise.reject(new Errors.HttpStatusError(423, 'Account has been locked'));
5+
throw ErrorAccountLocked;
86
}
97

10-
return Promise.resolve(data);
8+
return data;
119
};

src/utils/userData/getInternalData.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function handleNotFound(data) {
4949
}
5050

5151
function reduceData(data) {
52-
return reduce(data, reducer, {});
52+
return reduce(data, reducer, Object.create(null));
5353
}
5454

5555
function getInternalData(userKey, fetchData = true) {

src/utils/userData/getUserId.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
const getInternalData = require('./getInternalData');
2+
const isBanned = require('../isBanned');
23

3-
function getUserId(username) {
4-
return getInternalData
5-
.call(this, username, false)
6-
.get('id');
4+
async function getUserId(username, verifyBanned = false) {
5+
const internalData = await getInternalData
6+
.call(this, username, verifyBanned);
7+
8+
if (verifyBanned === true) {
9+
isBanned(internalData);
10+
}
11+
12+
return internalData.id;
713
}
814

915
module.exports = getUserId;

test/suites/activate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const is = require('is');
66
const sinon = require('sinon');
77

88
describe('#activate', function activateSuite() {
9-
const email = 'v@aminev.me';
9+
const email = 'spa@aminev.me';
1010

1111
beforeEach(global.startService);
1212
afterEach(global.clearRedis);

0 commit comments

Comments
 (0)