Added member API for removing email from suppression list (#15867)

closes TryGhost/Team#2306
This commit is contained in:
Elena Baidakova 2022-11-23 14:41:00 +04:00 committed by GitHub
parent 4b4592630f
commit 8d9d22e5a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 59 additions and 14 deletions

View File

@ -1,6 +1,7 @@
const _ = require('lodash');
const logging = require('@tryghost/logging');
const membersService = require('./service');
const emailSuppressionList = require('../email-suppression-list');
const models = require('../../models');
const urlUtils = require('../../../shared/url-utils');
const spamPrevention = require('../../web/shared/middleware/api/spam-prevention');
@ -39,7 +40,7 @@ const authMemberByUuid = async function (req, res, next) {
// Already authenticated via session
return next();
}
throw new errors.UnauthorizedError({
messsage: tpl(messages.missingUuid)
});
@ -97,6 +98,20 @@ const getMemberData = async function (req, res) {
}
};
const deleteSuppression = async function (req, res) {
try {
const member = await membersService.ssr.getMemberDataFromSession(req, res);
await emailSuppressionList.removeEmail(member.email);
res.writeHead(204);
res.end();
} catch (err) {
res.writeHead(err.statusCode, {
'Content-Type': 'text/plain;charset=UTF-8'
});
res.end(err.message);
}
};
const getMemberNewsletters = async function (req, res) {
try {
const memberUuid = req.query.uuid;
@ -262,5 +277,6 @@ module.exports = {
getMemberData,
updateMemberData,
updateMemberNewsletters,
deleteSession
deleteSession,
deleteSuppression
};

View File

@ -1,3 +1,5 @@
const labsService = require('../../../shared/labs');
function formatNewsletterResponse(newsletters) {
return newsletters.map(({id, name, description, sort_order: sortOrder}) => {
return {id, name, description, sort_order: sortOrder};
@ -23,5 +25,10 @@ module.exports.formattedMemberResponse = function formattedMemberResponse(member
if (member.newsletters) {
data.newsletters = formatNewsletterResponse(member.newsletters);
}
if (labsService.isSet('suppressionList') && member.email_suppression) {
data.email_suppression = member.email_suppression;
}
return data;
};

View File

@ -45,18 +45,21 @@ module.exports = function setupMembersApp() {
membersApp.put('/api/member', bodyParser.json({limit: '50mb'}), middleware.updateMemberData);
membersApp.post('/api/member/email', bodyParser.json({limit: '50mb'}), (req, res) => membersService.api.middleware.updateEmailAddress(req, res));
// Remove email from suppression list
membersApp.delete('/api/member/suppression', labs.enabledMiddleware('suppressionList'), middleware.deleteSuppression);
// Manage session
membersApp.get('/api/session', middleware.getIdentityToken);
membersApp.delete('/api/session', middleware.deleteSession);
// NOTE: this is wrapped in a function to ensure we always go via the getter
membersApp.post(
'/api/send-magic-link',
bodyParser.json(),
'/api/send-magic-link',
bodyParser.json(),
// Prevent brute forcing email addresses (user enumeration)
shared.middleware.brute.membersAuthEnumeration,
shared.middleware.brute.membersAuthEnumeration,
// Prevent brute forcing passwords for the same email address
shared.middleware.brute.membersAuth,
shared.middleware.brute.membersAuth,
(req, res, next) => membersService.api.middleware.sendMagicLink(req, res, next)
);
membersApp.post('/api/create-stripe-checkout-session', (req, res, next) => membersService.api.middleware.createCheckoutSession(req, res, next));
@ -68,11 +71,11 @@ module.exports = function setupMembersApp() {
// Feedback
membersApp.post(
'/api/feedback',
labs.enabledMiddleware('audienceFeedback'),
bodyParser.json({limit: '50mb'}),
middleware.loadMemberSession,
middleware.authMemberByUuid,
'/api/feedback',
labs.enabledMiddleware('audienceFeedback'),
bodyParser.json({limit: '50mb'}),
middleware.loadMemberSession,
middleware.authMemberByUuid,
http(api.feedbackMembers.add)
);

View File

@ -4,6 +4,10 @@ exports[`Comments API when authenticated can get member data 1: [body] 1`] = `
Object {
"avatar_image": null,
"email": "member@example.com",
"email_suppression": Object {
"info": null,
"suppressed": false,
},
"enable_comment_notifications": true,
"expertise": null,
"firstname": null,
@ -33,7 +37,7 @@ exports[`Comments API when authenticated can get member data 2: [headers] 1`] =
Object {
"access-control-allow-origin": "*",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "436",
"content-length": "489",
"content-type": "application/json; charset=utf-8",
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"vary": "Accept-Encoding",

View File

@ -196,5 +196,12 @@ describe('Comments API', function () {
member = await models.Member.findOne({id: member.id}, {require: true});
member.get('enable_comment_notifications').should.eql(true);
});
it('can remove member from suppression list', async function () {
await membersAgent
.delete(`/api/member/suppression`)
.expectStatus(204)
.expectEmptyBody();
});
});
});

View File

@ -24,7 +24,11 @@ describe('Members Service - utils', function () {
subscribed: true,
status: 'free',
extra: 'property',
enable_comment_notifications: true
enable_comment_notifications: true,
email_suppression: {
suppressed: false,
info: null
}
});
should(member1).deepEqual({
uuid: 'uuid-1',
@ -36,7 +40,11 @@ describe('Members Service - utils', function () {
subscribed: true,
subscriptions: [],
paid: false,
enable_comment_notifications: true
enable_comment_notifications: true,
email_suppression: {
suppressed: false,
info: null
}
});
});