Merge branch 'gdpr-delete' of github.com:coralproject/talk into gdpr-delete

* 'gdpr-delete' of github.com:coralproject/talk:
  typo
  cleaned up config as js instead of json file
  added space
  added durations to configuration file
  Update copy to reflect ability to take action up until account is deleted
  added docs
  Added TALK_ prefix to constants RECAPTCHA_WINDOW and RECAPTCHA_INCORRECT_TRIGGER
  RECAPTCHA_WINDOW and RECAPTCHA_INCORRECT_TRIGGER now can be set with env vars
This commit is contained in:
okbel
2018-05-03 15:17:04 -03:00
10 changed files with 62 additions and 23 deletions
+7
View File
@@ -212,6 +212,13 @@ const CONFIG = {
RECAPTCHA_PUBLIC: process.env.TALK_RECAPTCHA_PUBLIC,
RECAPTCHA_SECRET: process.env.TALK_RECAPTCHA_SECRET,
// RECAPTCHA_WINDOW is the rate limit's time interval
RECAPTCHA_WINDOW: process.env.TALK_RECAPTCHA_WINDOW || '10m',
// After RECAPTCHA_INCORRECT_TRIGGER incorrect attempts, recaptcha will be required.
RECAPTCHA_INCORRECT_TRIGGER:
process.env.TALK_RECAPTCHA_INCORRECT_TRIGGER || 5,
// WEBSOCKET_LIVE_URI is the absolute url to the live endpoint.
WEBSOCKET_LIVE_URI: process.env.TALK_WEBSOCKET_LIVE_URI || null,
+13 -1
View File
@@ -316,6 +316,18 @@ default to providing only a time based lockout. Refer to
[reCAPTCHA](https://www.google.com/recaptcha/intro/index.html) for information
on getting an account setup.
## TALK_RECAPTCHA_WINDOW
The rate limit time interval that there can be [TALK_RECAPTCHA_INCORRECT_TRIGGER](#talk_recaptcha_incorrect_trigger) incorrect attempts until the reCAPTCHA is
marked as required, parsed by
[ms](https://www.npmjs.com/package/ms). (Default `10m`)
## TALK_RECAPTCHA_INCORRECT_TRIGGER
The number of times that an incorrect login can be entered before within a time
perioud indicated by [TALK_RECAPTCHA_WINDOW](#talk_recaptcha_window) until the
reCAPTCHA is marked as required. (Default `5`)
## TALK_REDIS_CLIENT_CONFIGURATION
Configuration overrides for the redis client configuration in a JSON encoded
@@ -531,4 +543,4 @@ Sets the logging level for the context logger (from [Bunyan](https://github.com/
A JSON string representing the configuration passed to the
[fetch](https://www.npmjs.com/package/node-fetch) call for the scraper. It
can be used to set an authorization header, or change the user agent. (Default
`{}`)
`{}`)
@@ -6,6 +6,7 @@ import moment from 'moment';
import { Button, Icon } from 'plugin-api/beta/client/components/ui';
import styles from './AccountDeletionRequestedSign.css';
import { getErrorMessages } from 'coral-framework/utils';
import { scheduledDeletionDelayHours } from '../../config';
class AccountDeletionRequestedSign extends React.Component {
cancelAccountDeletion = async () => {
@@ -25,7 +26,7 @@ class AccountDeletionRequestedSign extends React.Component {
'MMM Do YYYY, h:mm a'
);
const deletionScheduledOn = moment(scheduledDeletionDate)
.subtract(24, 'hours')
.subtract(scheduledDeletionDelayHours, 'hours')
.format('MMM Do YYYY, h:mm a');
return (
@@ -33,7 +33,7 @@ const DeleteMyAccountFinalStep = props => (
<p className={styles.description}>
<strong>{t('delete_request.tell_us_why')}.</strong>{' '}
{t('delete_request.feedback_copy')}
{t('delete_request.feedback_copy')}{' '}
<a href={`mailto:${props.organizationContactEmail}`}>
{props.organizationContactEmail}
</a>.
@@ -4,16 +4,17 @@ import cn from 'classnames';
import { Button } from 'plugin-api/beta/client/components/ui';
import styles from './DeleteMyAccountStep.css';
import { t } from 'plugin-api/beta/client/services';
import { scheduledDeletionDelayHours } from '../../config';
const DeleteMyAccountStep1 = props => (
<div className={styles.step}>
<h4 className={styles.subTitle}>{t('delete_request.step_1.subtitle')}</h4>
<p className={styles.description}>
{t('delete_request.step_1.description')}
{t('delete_request.step_1.description', scheduledDeletionDelayHours)}
</p>
<h4 className={styles.subTitle}>{t('delete_request.step_1.subtitle_2')}</h4>
<p className={styles.description}>
{t('delete_request.step_1.description_2')}
{t('delete_request.step_1.description_2', scheduledDeletionDelayHours)}
</p>
<div className={cn(styles.actions)}>
<Button
@@ -4,6 +4,7 @@ import { t } from 'plugin-api/beta/client/services';
import { Button } from 'plugin-api/beta/client/components/ui';
import styles from './DownloadCommentHistory.css';
import { getErrorMessages } from 'coral-framework/utils';
import { downloadRateLimitDays } from '../../config';
export const readableDuration = durAsHours => {
const durAsDays = Math.ceil(durAsHours / 24);
@@ -42,7 +43,8 @@ class DownloadCommentHistory extends Component {
lastAccountDownload && new Date(lastAccountDownload);
const hoursLeft = lastAccountDownloadDate
? Math.ceil(
7 * 24 - (now.getTime() - lastAccountDownloadDate.getTime()) / 3.6e6
downloadRateLimitDays * 24 -
(now.getTime() - lastAccountDownloadDate.getTime()) / 3.6e6
)
: 0;
const canRequestDownload = !lastAccountDownloadDate || hoursLeft <= 0;
@@ -52,7 +54,7 @@ class DownloadCommentHistory extends Component {
<h3>{t('download_request.section_title')}</h3>
<p>
{t('download_request.you_will_get_a_copy')}{' '}
<b>{t('download_request.download_rate')}</b>.
<b>{t('download_request.download_rate', downloadRateLimitDays)}</b>.
</p>
{lastAccountDownloadDate && (
<p className={styles.most_recent}>
@@ -2,7 +2,7 @@ en:
download_request:
section_title: "Download My Comment History"
you_will_get_a_copy: "You will recieve an email with a link to download your comment history. You can make"
download_rate: "one download request every 7 days"
download_rate: "one download request every {0} days"
most_recent_request: "Your most recent request"
request: "Request Comment History"
rate_limit: "You can submit another Comment History request in {0}"
@@ -12,10 +12,10 @@ en:
day: "{0} day"
download_preparing: "Account Download Preparing - Check your email shortly for a download link"
delete_request:
account_deletion_cancelled: 'Account Deletion Request Cancelled - Your request to delete your account has been cancelled. You may now write comments, reply to comments, and select reactions.'
account_deletion_cancelled: 'Account Deletion Request Cancelled - Your request to delete your account has been cancelled.'
account_deletion_requested: 'Account Deletion Requested'
received_on: "A request to delete your account was received on "
cancel_request_description: "If you would like to continue leaving comments, replies or reactions, you may cancel your request to delete your account below"
cancel_request_description: "If you would like to reactivate your account, you may cancel your request to delete your account below"
before: "before"
cancel_account_deletion_request: "Cancel Account Deletion Request"
delete_my_account: "Delete My Account"
@@ -38,9 +38,9 @@ en:
item_3: "Your username and email address are removed from our system"
step_1:
subtitle: "When will my account be deleted?"
description: "Your account will be deleted 24 hours after your request has been submitted."
description: "Your account will be deleted {0} hours after your request has been submitted."
subtitle_2: "Can I still write comments until my account is deleted?"
description_2: "No. Once you have requested account deletion, you can no longer write comments, reply to comments, or select reactions."
description_2: "Yes, you can still comment, reply, and react to comments until the {0} hours expires."
step_2:
description: "Before your account is deleted, we recommend you download your comment history for your records. After your account is deleted, you will be unable to request your comment history."
to_download: "To download your comment history go to:"
@@ -0,0 +1,4 @@
module.exports = {
scheduledDeletionDelayHours: 24,
downloadRateLimitDays: 7,
};
@@ -8,6 +8,10 @@ const {
} = require('./errors');
const { ErrNotAuthorized, ErrMaxRateLimit } = require('errors');
const { URL } = require('url');
const {
scheduledDeletionDelayHours,
downloadRateLimitDays,
} = require('../config');
// generateDownloadLinks will generate a signed set of links for a given user to
// download an archive of their data.
@@ -42,21 +46,26 @@ async function sendDownloadLink(ctx) {
} = ctx;
// downloadLinkLimiter can be used to limit downloads for the user's data to
// once every 7 days.
const downloadLinkLimiter = new Limit('profileDataDownloadLimiter', 1, '7d');
// once every ${downloadRateLimitDays} days.
const downloadLinkLimiter = new Limit(
'profileDataDownloadLimiter',
1,
`${downloadRateLimitDays}d`
);
// Check that the user has not already requested a download within the last
// 7 days.
// ${downloadRateLimitDays} days.
const attempts = await downloadLinkLimiter.get(user.id);
if (attempts && attempts >= 1) {
throw new ErrMaxRateLimit();
}
// Check if the lastAccountDownload time is within 7 days.
// Check if the lastAccountDownload time is within ${downloadRateLimitDays}
// days.
if (
user.lastAccountDownload &&
moment(user.lastAccountDownload)
.add(7, 'days')
.add(downloadRateLimitDays, 'days')
.isAfter(moment())
) {
throw new ErrMaxRateLimit();
@@ -93,16 +102,17 @@ async function sendDownloadLink(ctx) {
}
// requestDeletion will schedule the current user to have their account deleted
// by setting the `scheduledDeletionDate` on the user 24 hours from now.
// by setting the `scheduledDeletionDate` on the user
// ${scheduledDeletionDelayHours} hours from now.
async function requestDeletion({ user, connectors: { models: { User } } }) {
// Ensure the user doesn't already have a deletion scheduled.
if (get(user, 'metadata.scheduledDeletionDate')) {
throw new ErrDeletionAlreadyScheduled();
}
// Get the date in the future 24 hours from now.
// Get the date in the future ${scheduledDeletionDelayHours} hours from now.
const scheduledDeletionDate = moment()
.add(24, 'hours')
.add(scheduledDeletionDelayHours, 'hours')
.toDate();
// Amend the scheduledDeletionDate on the user.
+5 -3
View File
@@ -18,12 +18,14 @@ const {
ErrCannotIgnoreStaff,
} = require('../errors');
const { difference, sample, some, merge, random } = require('lodash');
const { ROOT_URL } = require('../config');
const {
ROOT_URL,
RECAPTCHA_WINDOW,
RECAPTCHA_INCORRECT_TRIGGER,
} = require('../config');
const { jwt: JWT_SECRET } = require('../secrets');
const debug = require('debug')('talk:services:users');
const User = require('../models/user');
const RECAPTCHA_WINDOW = '10m'; // 10 minutes.
const RECAPTCHA_INCORRECT_TRIGGER = 5; // after 5 incorrect attempts, recaptcha will be required.
const Actions = require('./actions');
const mailer = require('./mailer');
const i18n = require('./i18n');