Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

Commit

Permalink
Merge pull request #124 from Human-Connection/blacklist_feature
Browse files Browse the repository at this point in the history
Blacklist other user accounts
  • Loading branch information
roschaefer authored Oct 5, 2018
2 parents bf3f51b + 385380d commit e95b29c
Show file tree
Hide file tree
Showing 16 changed files with 529 additions and 18 deletions.
2 changes: 1 addition & 1 deletion features/api/post.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: Import a post from an organization and publish it in Human Connection


Background:
Given there is a user in Human Connection with these credentials:
Given this is your user account:
| email | password |
| user@example.com | 1234 |

Expand Down
2 changes: 1 addition & 1 deletion features/api/usersettings.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Feature: Save current newsfeed filters to usersettings
In order to see the same selection of content next time I log in

Background:
Given there is a user in Human Connection with these credentials:
Given this is your user account:
| email | password | isVerified |
| user@example.com | 1234 | true |

Expand Down
43 changes: 43 additions & 0 deletions features/api/usersettings/blacklist.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
Feature: Individual Blacklist
As a user
I want to click on a button to blacklist certain user profiles
In order to stop seeing user content of this account, because I don't like them

Background:
Given this is your user account:
| email | password | isVerified |
| user@example.com | 1234 | true |
And these user accounts exist:
| name | email | isVerified |
| Troll | troll@example.com | true |
| Legit | legit@example.com | true |
And you are authenticated


Scenario: Blacklist a user
When you create your user settings via POST request to "/usersettings" with:
"""
{
"blacklist": ["5b5863e8d47c14323165718b"]
}
"""
Then you will stop seeing posts of the user with id "5b5863e8d47c14323165718b"

Scenario: Filter out contributions of a blacklisted user
Given you blacklisted the user "Troll" before
When this user publishes a post
And you read your current news feed
Then this post is not included

Scenario: Show but conceal comments of a blacklisted user
Given you blacklisted the user "Troll" before
And there is a post "Hello World" by user "Legit"
And the blacklisted user wrote a comment on that post:
"""
I hate you
"""
When you read through the comments of that post
Then you will see a hint instead of a comment:
"""
Comments of this blacklisted user are not visible.
"""
37 changes: 37 additions & 0 deletions features/api/usersettings/language.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Feature: Save preferred language settings
As a user of human connection
You would like to have my preferred language saved
So when I log in the next time, the UI switches to my language automatically

Background:
Given this is your user account:
| email | password | isVerified |
| user@example.com | 1234 | true |

Scenario: Save your language
Given you are authenticated
When you create your user settings via POST request to "/usersettings" with:
"""
{
"uiLanguage": "de"
}
"""
Then your language "de" is stored in your user settings

Scenario: Save your filter settings
Given you are authenticated
When you create your user settings via POST request to "/usersettings" with:
"""
{
"contentLanguages" : [ "en" ],
"uiLanguage" : "en",
"filter": {
"categoryIds": [
"5b310ab8b801653c1eb6c426",
"5b310ab8b801653c1eb6c427",
"5b310ab8b801653c1eb6c428"
]
}
}
"""
Then these category ids are stored in your user settings
98 changes: 94 additions & 4 deletions features/step_definitions/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ let currentUser;
let currentUserPassword;
let httpResponse;
let currentUserAccessToken;
let lastPost;
let blacklistedUser;

function authenticate(email, plainTextPassword) {
const formData = {
Expand All @@ -35,10 +37,23 @@ function postRequest(route, body, callback) {
body,
headers: { 'Content-Type': 'application/json' },
};
return request(params, route, callback);
}

function getRequest(route, callback){
const params = {
method: 'get',
headers: { 'Content-Type': 'application/json' },
};
return request(params, route, callback);
}

function request(params, route, callback) {
const requestParams = Object.assign({}, params);
if (currentUserAccessToken) {
params.headers.Authorization = `Bearer ${currentUserAccessToken}`;
requestParams.headers.Authorization = `Bearer ${currentUserAccessToken}`;
}
fetch(`${hcBackendUrl}${route}`, params)
fetch(`${hcBackendUrl}${route}`, requestParams)
.then(response => response.json())
.catch((err) => {
throw (err);
Expand All @@ -49,14 +64,21 @@ function postRequest(route, body, callback) {
});
}

Given('there is a user in Human Connection with these credentials:', function (dataTable) {

Given('this is your user account:', function (dataTable) {
const params = dataTable.hashes()[0];
currentUserPassword = params.password;
return this.app.service('users').create(params).then((user) => {
currentUser = user;
});
});

Given('these user accounts exist:', function (dataTable) {
return Promise.all(dataTable.hashes().map(params => {
return this.app.service('users').create(params);
}));
});

Given('you are authenticated', () => authenticate(currentUser.email, currentUserPassword).then((accessToken) => {
currentUserAccessToken = accessToken;
}));
Expand Down Expand Up @@ -92,7 +114,7 @@ Then('these category ids are stored in your user settings', function () {
});

Then('your language {string} is stored in your user settings', function (lang) {
return this.app.service('usersettings').find({userId: currentUser._id.toString()}).then((settings) => {
return this.app.service('usersettings').find({query: {userId: currentUser._id.toString()}}).then((settings) => {
expect(settings.total).to.eq(1);
expect(settings.data[0].uiLanguage).to.eq(lang);
});
Expand All @@ -109,3 +131,71 @@ When('you create your user settings via POST request to {string} with:', functio
postRequest(route, JSON.stringify(jsonBody), callback);
});

Then('you will stop seeing posts of the user with id {string}', function (blacklistedUserId) {
return this.app.service('usersettings').find({userId: currentUser._id.toString()}).then((settings) => {
expect(settings.total).to.eq(1);
expect(settings.data[0].blacklist).to.be.an('array').that.does.include(blacklistedUserId);
});
});

Given('you blacklisted the user {string} before', async function (blacklistedUserName) {
const res = await this.app.service('users').find({query: {name: blacklistedUserName}});
blacklistedUser = res.data[0];
const params = {
userId: currentUser._id,
blacklist: [blacklistedUser._id]
};
return this.app.service('usersettings').create(params);
});

When('this user publishes a post', function () {
const params = {
title: 'Awful title',
content: 'disgusting content',
language: 'en',
type: 'post',
userId: blacklistedUser._id
};
return this.app.service('contributions').create(params);
});

When('you read your current news feed', function (callback) {
getRequest('/contributions', callback);
});

Then('this post is not included', function () {
expect(httpResponse.data).to.be.an('array').that.is.empty;
});

Given('there is a post {string} by user {string}', async function (postTitle, userName) {
const users = await this.app.service('users').find({ query: {name: userName} });
const user = users.data[0];
const params = {
title: postTitle,
content: 'blah',
language: 'en',
type: 'post',
userId: user._id
};
lastPost = await this.app.service('contributions').create(params);
return lastPost;
});

Given('the blacklisted user wrote a comment on that post:', function (comment) {
const commentParams = {
userId: blacklistedUser._id,
content: comment,
contributionId: lastPost._id
};
return this.app.service('comments').create(commentParams);
});

When('you read through the comments of that post', function (callback) {
getRequest('/comments', callback);
});

Then('you will see a hint instead of a comment:', function (hint) {
const comment = httpResponse.data[0];
expect(comment.content).to.eq(hint);
expect(comment.contentExcerpt).to.eq(hint);
});
2 changes: 1 addition & 1 deletion server/helper/alter-items.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ module.exports = func => hook => {
}
replaceItems(hook, items);
return hook;
};
};
40 changes: 40 additions & 0 deletions server/hooks/conceal-blacklisted-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const alterItems = require('../helper/alter-items');

const defaults = {
blacklist: [],
data: {
content: 'You have blacklisted this user'
}
};

const handleItem = options => item => {
if (options.blacklist){
let found = options.blacklist.find((userId) => {
return String(userId) === item.userId;
});
if (found){
item = {...item, ...options.data};
}
}
return item;
};

module.exports = function concealBlacklistedData(options = defaults) {
return async function(hook){
if (hook.method === 'find' || hook.id === null) {
const authenticatedUser = hook.params.user;
if (!authenticatedUser){
return hook;
}
const usersettings = await hook.app.service('usersettings').find({query: {userId: authenticatedUser._id}});
if (usersettings.total <= 0){
return hook;
}
options.blacklist = usersettings.data[0].blacklist;
return alterItems(handleItem(options))(hook);
}

return hook;
};
};

23 changes: 23 additions & 0 deletions server/hooks/exclude-blacklisted.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module.exports = function excludeBlacklisted() {
return async function (hook) {
if (hook.type !== 'before') {
throw new Error('The \'excludeBlacklisted\' hook should only be used as a \'before\' hook.');
}

if (hook.method === 'find' || hook.id === null) {
const authenticatedUser = hook.params.user;
if (!authenticatedUser){
return hook;
}
const usersettings = await hook.app.service('usersettings').find({query: {userId: authenticatedUser._id}});
if (usersettings.total <= 0){
return hook;
}
const blacklist = usersettings.data[0].blacklist;
hook.params.query.userId = {$nin: blacklist};
return hook;
}

return hook;
};
};
1 change: 1 addition & 0 deletions server/models/usersettings.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = function (app) {
const mongooseClient = app.get('mongooseClient');
const usersettings = new mongooseClient.Schema({
userId: {type: String, required: true, unique: true},
blacklist: {type: Array, default: []},
uiLanguage: {type: String, required: true},
contentLanguages: {type: Array, default: []},
filter: {
Expand Down
32 changes: 28 additions & 4 deletions server/services/comments/comments.hooks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { authenticate } = require('@feathersjs/authentication').hooks;
const { unless, isProvider, populate, discard, softDelete, setNow } = require('feathers-hooks-common');
const { iff, unless, isProvider, populate, discard, softDelete, setNow } = require('feathers-hooks-common');
const { protect } = require('@feathersjs/authentication-local').hooks;
const {
//queryWithCurrentUser,
Expand All @@ -10,6 +10,7 @@ const {
const { isVerified } = require('feathers-authentication-management').hooks;
const createExcerpt = require('../../hooks/create-excerpt');
const patchDeletedData = require('../../hooks/patch-deleted-data');
const concealBlacklistedData = require('../../hooks/conceal-blacklisted-data');
const keepDeletedDataFields = require('../../hooks/keep-deleted-data-fields');
const createNotifications = require('./hooks/create-notifications');
const createMentionNotifications = require('./hooks/create-mention-notifications');
Expand Down Expand Up @@ -41,12 +42,21 @@ module.exports = {
],
find: [
// We want to deleted comments to show up
iff(
hook => hook.params.headers && hook.params.headers.authorization,
authenticate('jwt')
),
hook => {
delete hook.params.query.deleted;
return hook;
}
],
get: [],
get: [
iff(
hook => hook.params.headers && hook.params.headers.authorization,
authenticate('jwt')
)
],
create: [
authenticate('jwt'),
// Allow seeder to seed comments
Expand Down Expand Up @@ -109,10 +119,24 @@ module.exports = {
],
find: [
populate({ schema: userSchema }),
protect('content', 'badgeIds')
protect('content', 'badgeIds'),
concealBlacklistedData({
data: {
content: 'Comments of this blacklisted user are not visible.',
contentExcerpt: 'Comments of this blacklisted user are not visible.',
hasMore: false
}
})
],
get: [
populate({ schema: userSchema })
populate({ schema: userSchema }),
concealBlacklistedData({
data: {
content: 'Comments of this blacklisted user are not visible.',
contentExcerpt: 'Comments of this blacklisted user are not visible.',
hasMore: false
}
})
],
create: [
populate({ schema: userSchema }),
Expand Down
Loading

0 comments on commit e95b29c

Please sign in to comment.