mirror of
https://github.com/hcengineering/platform.git
synced 2025-05-30 12:20:00 +00:00
UBERF-9540: Fix invite message and add rate limit (#8123)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
a6b8d6cd09
commit
6a926a7ea7
@ -135,6 +135,7 @@
|
||||
"StartOfTheWeek": "Začátek týdne",
|
||||
"SystemSetupString": "Nastavení systému ({day})",
|
||||
"DefaultString": "Výchozí ({day})",
|
||||
"AddAttribute": "Přidat atribut"
|
||||
"AddAttribute": "Přidat atribut",
|
||||
"WorkspaceNamePattern": "Název musí být 40 znaků nebo méně, není prázdný a nesmí obsahovat speciální znaky (<, >, /)"
|
||||
}
|
||||
}
|
@ -135,6 +135,7 @@
|
||||
"StartOfTheWeek": "Wochenstart",
|
||||
"SystemSetupString": "Systemkonfiguration ({day})",
|
||||
"DefaultString": "Stillschweigend ({day})",
|
||||
"AddAttribute": "Attribut hinzufügen"
|
||||
"AddAttribute": "Attribut hinzufügen",
|
||||
"WorkspaceNamePattern": "Name muss 40 Zeichen oder weniger sein, nicht leer sein und keine Sonderzeichen (<, >, /) enthalten"
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,7 @@
|
||||
"StartOfTheWeek": "Start of the week",
|
||||
"SystemSetupString": "System Setup ({day})",
|
||||
"DefaultString": "Default ({day})",
|
||||
"AddAttribute": "Add attribute"
|
||||
"AddAttribute": "Add attribute",
|
||||
"WorkspaceNamePattern": "Name must be 40 characters or less, not empty, and cannot contain special characters (<, >, /)"
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +126,7 @@
|
||||
"StartOfTheWeek": "Inicio de la semana",
|
||||
"SystemSetupString": "Configuración del sistema ({day})",
|
||||
"DefaultString": "Predeterminado ({day})",
|
||||
"AddAttribute": "Añadir atributo"
|
||||
"AddAttribute": "Añadir atributo",
|
||||
"WorkspaceNamePattern": "El nombre debe tener 40 caracteres o menos, no esté vacío y no puede contener caracteres especiales (<, >, /)"
|
||||
}
|
||||
}
|
@ -135,6 +135,7 @@
|
||||
"StartOfTheWeek": "Début de semaine",
|
||||
"SystemSetupString": "Configuration du système ({day})",
|
||||
"DefaultString": "Par défaut ({day})",
|
||||
"AddAttribute": "Ajouter un attribut"
|
||||
"AddAttribute": "Ajouter un attribut",
|
||||
"WorkspaceNamePattern": "Le nom doit comporter 40 caractères ou moins, ne doit pas être vide et ne doit pas contenir de caractères spéciaux (<, >, /)"
|
||||
}
|
||||
}
|
@ -135,6 +135,7 @@
|
||||
"StartOfTheWeek": "Inizio settimana",
|
||||
"SystemSetupString": "Configurazione del sistema ({day})",
|
||||
"DefaultString": "Predefinito ({day})",
|
||||
"AddAttribute": "Aggiungi attributo"
|
||||
"AddAttribute": "Aggiungi attributo",
|
||||
"WorkspaceNamePattern": "Il nome deve essere di 40 caratteri o meno, non vuoto e non può contenere caratteri speciali (<, >, /)"
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +126,7 @@
|
||||
"StartOfTheWeek": "Início da semana",
|
||||
"SystemSetupString": "Configuração do sistema ({day})",
|
||||
"DefaultString": "Predefinição ({day})",
|
||||
"AddAttribute": "Adicionar atributo"
|
||||
"AddAttribute": "Adicionar atributo",
|
||||
"WorkspaceNamePattern": "O nome deve ter 40 caracteres ou menos, não está vazio e não pode conter caracteres especiais (<, >, /)"
|
||||
}
|
||||
}
|
@ -136,6 +136,7 @@
|
||||
"StartOfTheWeek": "Начало недели",
|
||||
"SystemSetupString": "Системная настройка ({day})",
|
||||
"DefaultString": "По умолчанию ({day})",
|
||||
"AddAttribute": "Добавить атрибут"
|
||||
"AddAttribute": "Добавить атрибут",
|
||||
"WorkspaceNamePattern": "Имя должно быть длиной 40 символов или меньше, не должно быть пустым и не может содержать специальные символы (<, >, /)"
|
||||
}
|
||||
}
|
||||
|
@ -135,6 +135,7 @@
|
||||
"StartOfTheWeek": "本周开始",
|
||||
"SystemSetupString": "系统设置({day})",
|
||||
"DefaultString": "违约({day})",
|
||||
"AddAttribute": "添加属性"
|
||||
"AddAttribute": "添加属性",
|
||||
"WorkspaceNamePattern": "名称必须为 40 个字符或更少,不能为空,不能包含特殊字符(<、>、/)"
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,14 @@
|
||||
let name: string = ''
|
||||
|
||||
const accountClient = getAccountClient()
|
||||
const disabledSet = ['\n', '<', '>', '/', '\\']
|
||||
|
||||
$: editNameDisabled =
|
||||
isEditingName &&
|
||||
(name.trim().length > 40 ||
|
||||
name.trim() === oldName ||
|
||||
name.trim() === '' ||
|
||||
disabledSet.some((it) => name.includes(it)))
|
||||
|
||||
void loadWorkspaceName()
|
||||
|
||||
@ -80,8 +88,6 @@
|
||||
isEditingName = false
|
||||
}
|
||||
|
||||
$: editNameDisabled = isEditingName && (name.trim() === oldName || name.trim() === '')
|
||||
|
||||
async function handleDelete (): Promise<void> {
|
||||
showPopup(MessageBox, {
|
||||
label: setting.string.DeleteWorkspace,
|
||||
@ -99,7 +105,7 @@
|
||||
let workspaceSettings: WorkspaceSetting | undefined = undefined
|
||||
|
||||
const client = getClient()
|
||||
client.findOne(setting.class.WorkspaceSetting, {}).then((r) => {
|
||||
void client.findOne(setting.class.WorkspaceSetting, {}).then((r) => {
|
||||
workspaceSettings = r
|
||||
})
|
||||
|
||||
|
@ -117,6 +117,7 @@ export default mergeIds(settingId, setting, {
|
||||
Calendar: '' as IntlString,
|
||||
StartOfTheWeek: '' as IntlString,
|
||||
SystemSetupString: '' as IntlString,
|
||||
DefaultString: '' as IntlString
|
||||
DefaultString: '' as IntlString,
|
||||
WorkspaceNamePattern: '' as IntlString
|
||||
}
|
||||
})
|
||||
|
@ -342,6 +342,8 @@ export async function createWorkspace (
|
||||
const { workspaceName, region } = params
|
||||
const { account } = decodeTokenVerbose(ctx, token)
|
||||
|
||||
checkRateLimit(account, workspaceName)
|
||||
|
||||
ctx.info('Creating workspace record', { workspaceName, account, region })
|
||||
|
||||
// Any confirmed social ID will do
|
||||
@ -410,6 +412,12 @@ export async function createInviteLink (
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Temporary solution to prevent spam using sendInvite
|
||||
const invitesSend = new Map<string, {
|
||||
lastSend: number
|
||||
totalSend: number
|
||||
}>()
|
||||
|
||||
export async function sendInvite (
|
||||
ctx: MeasureContext,
|
||||
db: AccountDB,
|
||||
@ -433,6 +441,8 @@ export async function sendInvite (
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspaceUuid }))
|
||||
}
|
||||
|
||||
checkRateLimit(account, workspaceUuid)
|
||||
|
||||
const expHours = 48
|
||||
const exp = expHours * 60 * 60 * 1000
|
||||
|
||||
@ -444,6 +454,34 @@ export async function sendInvite (
|
||||
ctx.info('Invite has been sent', { to: inviteEmail.to, workspaceUuid: workspace.uuid, workspaceName: workspace.name })
|
||||
}
|
||||
|
||||
function checkRateLimit (email: string, workspaceName: string): void {
|
||||
const now = Date.now()
|
||||
const lastInvites = invitesSend.get(email)
|
||||
if (lastInvites !== undefined) {
|
||||
lastInvites.totalSend++
|
||||
lastInvites.lastSend = now
|
||||
if (lastInvites.totalSend > 5 && (now - lastInvites.lastSend) < 60 * 1000) {
|
||||
// Less 60 seconds between invites
|
||||
throw new PlatformError(
|
||||
new Status(Severity.ERROR, platform.status.WorkspaceRateLimit, { workspace: workspaceName })
|
||||
)
|
||||
}
|
||||
invitesSend.delete(email)
|
||||
} else {
|
||||
invitesSend.set(email, {
|
||||
lastSend: now,
|
||||
totalSend: 1
|
||||
})
|
||||
}
|
||||
|
||||
// We need to cleanup map
|
||||
for (const [k, vv] of invitesSend.entries()) {
|
||||
if (vv.lastSend < now - 60 * 1000) {
|
||||
invitesSend.delete(k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function resendInvite (
|
||||
ctx: MeasureContext,
|
||||
db: AccountDB,
|
||||
@ -463,6 +501,8 @@ export async function resendInvite (
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspaceUuid }))
|
||||
}
|
||||
|
||||
checkRateLimit(account, workspaceUuid)
|
||||
|
||||
const expHours = 48
|
||||
const newExp = Date.now() + expHours * 60 * 60 * 1000
|
||||
|
||||
|
@ -1193,7 +1193,7 @@ export async function getInviteEmail (
|
||||
): Promise<EmailInfo> {
|
||||
const front = getFrontUrl(branding)
|
||||
const link = concatLink(front, `/login/join?inviteId=${inviteId}`)
|
||||
const ws = workspace.name !== '' ? workspace.name : workspace.url
|
||||
const ws = (workspace.name !== '' ? workspace.name : workspace.url).replace(/[<>/]/g, '').slice(0, 40)
|
||||
const lang = branding?.language
|
||||
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user