feat(syncing-server): reduced abuse thresholds for free users (#1021)
This commit is contained in:
parent
f830bac873
commit
0443de88ce
6 changed files with 78 additions and 24 deletions
|
@ -119,8 +119,10 @@ void container.load().then((container) => {
|
|||
container.get<boolean>(TYPES.Sync_STRICT_ABUSE_PROTECTION),
|
||||
container.get<number>(TYPES.Sync_ITEM_OPERATIONS_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES),
|
||||
container.get<number>(TYPES.Sync_ITEM_OPERATIONS_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_PAYLOAD_SIZE_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES),
|
||||
container.get<number>(TYPES.Sync_FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES),
|
||||
container.get<winston.Logger>(TYPES.Sync_Logger),
|
||||
)
|
||||
|
||||
|
|
|
@ -479,7 +479,14 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind(TYPES.Sync_ITEM_OPERATIONS_ABUSE_THRESHOLD)
|
||||
.toConstantValue(
|
||||
env.get('ITEM_OPERATIONS_ABUSE_THRESHOLD', true) ? +env.get('ITEM_OPERATIONS_ABUSE_THRESHOLD', true) : 500,
|
||||
env.get('ITEM_OPERATIONS_ABUSE_THRESHOLD', true) ? +env.get('ITEM_OPERATIONS_ABUSE_THRESHOLD', true) : 1000,
|
||||
)
|
||||
container
|
||||
.bind(TYPES.Sync_FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD)
|
||||
.toConstantValue(
|
||||
env.get('FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD', true)
|
||||
? +env.get('FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD', true)
|
||||
: 500,
|
||||
)
|
||||
container
|
||||
.bind(TYPES.Sync_ITEM_OPERATIONS_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES)
|
||||
|
@ -489,15 +496,24 @@ export class ContainerConfigLoader {
|
|||
: 5,
|
||||
)
|
||||
container
|
||||
.bind(TYPES.Sync_PAYLOAD_SIZE_ABUSE_THRESHOLD)
|
||||
.bind(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD)
|
||||
.toConstantValue(
|
||||
env.get('PAYLOAD_SIZE_ABUSE_THRESHOLD', true) ? +env.get('PAYLOAD_SIZE_ABUSE_THRESHOLD', true) : 20_000_000,
|
||||
env.get('UPLOAD_BANDWIDTH_ABUSE_THRESHOLD', true)
|
||||
? +env.get('UPLOAD_BANDWIDTH_ABUSE_THRESHOLD', true)
|
||||
: 100_000_000,
|
||||
)
|
||||
container
|
||||
.bind(TYPES.Sync_PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES)
|
||||
.bind(TYPES.Sync_FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD)
|
||||
.toConstantValue(
|
||||
env.get('PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES', true)
|
||||
? +env.get('PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES', true)
|
||||
env.get('FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD', true)
|
||||
? +env.get('FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD', true)
|
||||
: 50_000_000,
|
||||
)
|
||||
container
|
||||
.bind(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES)
|
||||
.toConstantValue(
|
||||
env.get('UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES', true)
|
||||
? +env.get('UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES', true)
|
||||
: 5,
|
||||
)
|
||||
container.bind(TYPES.Sync_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
|
@ -1145,8 +1161,10 @@ export class ContainerConfigLoader {
|
|||
container.get<boolean>(TYPES.Sync_STRICT_ABUSE_PROTECTION),
|
||||
container.get<number>(TYPES.Sync_ITEM_OPERATIONS_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES),
|
||||
container.get<number>(TYPES.Sync_ITEM_OPERATIONS_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_PAYLOAD_SIZE_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES),
|
||||
container.get<number>(TYPES.Sync_FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD),
|
||||
container.get<number>(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES),
|
||||
container.get<ControllerContainerInterface>(TYPES.Sync_ControllerContainer),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -47,9 +47,11 @@ const TYPES = {
|
|||
'Sync_ITEM_OPERATIONS_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES',
|
||||
),
|
||||
Sync_ITEM_OPERATIONS_ABUSE_THRESHOLD: Symbol.for('Sync_ITEM_OPERATIONS_ABUSE_THRESHOLD'),
|
||||
Sync_PAYLOAD_SIZE_ABUSE_THRESHOLD: Symbol.for('Sync_PAYLOAD_SIZE_ABUSE_THRESHOLD'),
|
||||
Sync_PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES: Symbol.for(
|
||||
'Sync_PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES',
|
||||
Sync_FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD: Symbol.for('Sync_FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD'),
|
||||
Sync_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD: Symbol.for('Sync_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD'),
|
||||
Sync_FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD: Symbol.for('Sync_FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD'),
|
||||
Sync_UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES: Symbol.for(
|
||||
'Sync_UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES',
|
||||
),
|
||||
// use cases
|
||||
Sync_SyncItems: Symbol.for('Sync_SyncItems'),
|
||||
|
|
|
@ -29,8 +29,11 @@ export class AnnotatedItemsController extends BaseItemsController {
|
|||
@inject(TYPES.Sync_ITEM_OPERATIONS_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES)
|
||||
override itemOperationsAbuseTimeframeLengthInMinutes: number,
|
||||
@inject(TYPES.Sync_ITEM_OPERATIONS_ABUSE_THRESHOLD) override itemOperationsAbuseThreshold: number,
|
||||
@inject(TYPES.Sync_PAYLOAD_SIZE_ABUSE_THRESHOLD) override payloadSizeAbuseThreshold: number,
|
||||
@inject(TYPES.Sync_PAYLOAD_SIZE_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES)
|
||||
@inject(TYPES.Sync_FREE_USERS_ITEM_OPERATIONS_ABUSE_THRESHOLD)
|
||||
override freeUsersItemOperationsAbuseThreshold: number,
|
||||
@inject(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD) override payloadSizeAbuseThreshold: number,
|
||||
@inject(TYPES.Sync_FREE_USERS_UPLOAD_BANDWIDTH_ABUSE_THRESHOLD) override freeUsersPayloadSizeAbuseThreshold: number,
|
||||
@inject(TYPES.Sync_UPLOAD_BANDWIDTH_ABUSE_TIMEFRAME_LENGTH_IN_MINUTES)
|
||||
override payloadSizeAbuseTimeframeLengthInMinutes: number,
|
||||
) {
|
||||
super(
|
||||
|
@ -44,7 +47,9 @@ export class AnnotatedItemsController extends BaseItemsController {
|
|||
strictAbuseProtection,
|
||||
itemOperationsAbuseTimeframeLengthInMinutes,
|
||||
itemOperationsAbuseThreshold,
|
||||
freeUsersItemOperationsAbuseThreshold,
|
||||
payloadSizeAbuseThreshold,
|
||||
freeUsersPayloadSizeAbuseThreshold,
|
||||
payloadSizeAbuseTimeframeLengthInMinutes,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ export class BaseItemsController extends BaseHttpController {
|
|||
protected strictAbuseProtection: boolean,
|
||||
protected itemOperationsAbuseTimeframeLengthInMinutes: number,
|
||||
protected itemOperationsAbuseThreshold: number,
|
||||
protected freeUsersItemOperationsAbuseThreshold: number,
|
||||
protected payloadSizeAbuseThreshold: number,
|
||||
protected freeUsersPayloadSizeAbuseThreshold: number,
|
||||
protected payloadSizeAbuseTimeframeLengthInMinutes: number,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
|
@ -46,7 +48,7 @@ export class BaseItemsController extends BaseHttpController {
|
|||
const checkForItemOperationsAbuseResult = await this.checkForTrafficAbuse.execute({
|
||||
metricToCheck: Metric.NAMES.ItemOperation,
|
||||
userUuid: locals.user.uuid,
|
||||
threshold: this.itemOperationsAbuseThreshold,
|
||||
threshold: locals.isFreeUser ? this.freeUsersItemOperationsAbuseThreshold : this.itemOperationsAbuseThreshold,
|
||||
timeframeLengthInMinutes: this.itemOperationsAbuseTimeframeLengthInMinutes,
|
||||
})
|
||||
if (checkForItemOperationsAbuseResult.isFailed()) {
|
||||
|
@ -54,14 +56,22 @@ export class BaseItemsController extends BaseHttpController {
|
|||
userId: locals.user.uuid,
|
||||
})
|
||||
if (this.strictAbuseProtection) {
|
||||
return this.json({ error: { message: checkForItemOperationsAbuseResult.getError() } }, 429)
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message:
|
||||
'You have exceeded the maximum bandwidth allotted to your account in a 5-minute period. Please wait to try again, or upgrade your account for increased limits.',
|
||||
},
|
||||
},
|
||||
429,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const checkForPayloadSizeAbuseResult = await this.checkForTrafficAbuse.execute({
|
||||
metricToCheck: Metric.NAMES.ContentSizeUtilized,
|
||||
userUuid: locals.user.uuid,
|
||||
threshold: this.payloadSizeAbuseThreshold,
|
||||
threshold: locals.isFreeUser ? this.freeUsersPayloadSizeAbuseThreshold : this.payloadSizeAbuseThreshold,
|
||||
timeframeLengthInMinutes: this.payloadSizeAbuseTimeframeLengthInMinutes,
|
||||
})
|
||||
if (checkForPayloadSizeAbuseResult.isFailed()) {
|
||||
|
@ -70,7 +80,15 @@ export class BaseItemsController extends BaseHttpController {
|
|||
})
|
||||
|
||||
if (this.strictAbuseProtection) {
|
||||
return this.json({ error: { message: checkForPayloadSizeAbuseResult.getError() } }, 429)
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message:
|
||||
'You have exceeded the maximum bandwidth allotted to your account in a 5-minute period. Please wait to try again, or upgrade your account for increased limits.',
|
||||
},
|
||||
},
|
||||
429,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ export class SyncingServer implements ISyncingServer {
|
|||
private strictAbuseProtection: boolean,
|
||||
private itemOperationsAbuseTimeframeLengthInMinutes: number,
|
||||
private itemOperationsAbuseThreshold: number,
|
||||
private freeUsersItemOperationsAbuseThreshold: number,
|
||||
private payloadSizeAbuseThreshold: number,
|
||||
private freeUsersPayloadSizeAbuseThreshold: number,
|
||||
private payloadSizeAbuseTimeframeLengthInMinutes: number,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
@ -32,11 +34,12 @@ export class SyncingServer implements ISyncingServer {
|
|||
): Promise<void> {
|
||||
try {
|
||||
const userUuid = call.metadata.get('x-user-uuid').pop() as string
|
||||
const isFreeUser = call.metadata.get('x-is-free-user').pop() === 'true'
|
||||
|
||||
const checkForItemOperationsAbuseResult = await this.checkForTrafficAbuse.execute({
|
||||
metricToCheck: Metric.NAMES.ItemOperation,
|
||||
userUuid,
|
||||
threshold: this.itemOperationsAbuseThreshold,
|
||||
threshold: isFreeUser ? this.freeUsersItemOperationsAbuseThreshold : this.itemOperationsAbuseThreshold,
|
||||
timeframeLengthInMinutes: this.itemOperationsAbuseTimeframeLengthInMinutes,
|
||||
})
|
||||
if (checkForItemOperationsAbuseResult.isFailed()) {
|
||||
|
@ -45,7 +48,10 @@ export class SyncingServer implements ISyncingServer {
|
|||
})
|
||||
if (this.strictAbuseProtection) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-sync-error-message', checkForItemOperationsAbuseResult.getError())
|
||||
metadata.set(
|
||||
'x-sync-error-message',
|
||||
'You have exceeded the maximum bandwidth allotted to your account in a 5-minute period. Please wait to try again, or upgrade your account for increased limits.',
|
||||
)
|
||||
metadata.set('x-sync-error-response-code', '429')
|
||||
|
||||
return callback(
|
||||
|
@ -63,7 +69,7 @@ export class SyncingServer implements ISyncingServer {
|
|||
const checkForPayloadSizeAbuseResult = await this.checkForTrafficAbuse.execute({
|
||||
metricToCheck: Metric.NAMES.ContentSizeUtilized,
|
||||
userUuid,
|
||||
threshold: this.payloadSizeAbuseThreshold,
|
||||
threshold: isFreeUser ? this.freeUsersPayloadSizeAbuseThreshold : this.payloadSizeAbuseThreshold,
|
||||
timeframeLengthInMinutes: this.payloadSizeAbuseTimeframeLengthInMinutes,
|
||||
})
|
||||
if (checkForPayloadSizeAbuseResult.isFailed()) {
|
||||
|
@ -73,7 +79,10 @@ export class SyncingServer implements ISyncingServer {
|
|||
|
||||
if (this.strictAbuseProtection) {
|
||||
const metadata = new grpc.Metadata()
|
||||
metadata.set('x-sync-error-message', checkForPayloadSizeAbuseResult.getError())
|
||||
metadata.set(
|
||||
'x-sync-error-message',
|
||||
'You have exceeded the maximum bandwidth allotted to your account in a 5-minute period. Please wait to try again, or upgrade your account for increased limits.',
|
||||
)
|
||||
metadata.set('x-sync-error-response-code', '429')
|
||||
|
||||
return callback(
|
||||
|
@ -158,7 +167,7 @@ export class SyncingServer implements ISyncingServer {
|
|||
readOnlyAccess,
|
||||
sessionUuid: call.metadata.get('x-session-uuid').pop() as string,
|
||||
sharedVaultUuids,
|
||||
isFreeUser: call.metadata.get('x-is-free-user').pop() === 'true',
|
||||
isFreeUser,
|
||||
})
|
||||
if (syncResult.isFailed()) {
|
||||
const metadata = new grpc.Metadata()
|
||||
|
|
Loading…
Reference in a new issue