mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-12 04:37:44 +02:00
Use huly id for caldav, disable app secret getting for accounts (#8581)
Signed-off-by: Nikolay Chunosov <Chunosov.N@gmail.com>
This commit is contained in:
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Trvání schůzky",
|
||||
"MeetingInterval": "Pauza mezi schůzkami",
|
||||
"CalDavAccess": "Přístup CalDAV",
|
||||
"CalDavAccessPrompt": "Protokol CalDAV umožňuje synchronizaci vašeho osobního kalendáře Huly s externími aplikacemi kalendáře. Vyberte účet a použijte navrhované heslo pro registraci serveru Huly CalDAV v klientské aplikaci kalendáře.",
|
||||
"CalDavAccessPrompt": "Protokol CalDAV umožňuje synchronizaci vašeho osobního kalendáře Huly s externími kalendářovými aplikacemi. Použijte tento účet a navrhované heslo pro registraci serveru Huly CalDAV v aplikaci kalendářového klienta.",
|
||||
"CalDavAccessEnable": "Povolte přístup CalDAV k osobnímu kalendáři v pracovním prostoru {ws}",
|
||||
"CalDavAccessServer": "Server CalDAV",
|
||||
"CalDavAccessAccount": "Účet Huly",
|
||||
"CalDavAccessPassword": "Heslo (automaticky vygenerované)"
|
||||
"CalDavAccessPassword": "Heslo",
|
||||
"CalDavAccessPasswordWarning": "Tohle heslo už nebudete moci znovu vidět. Uložte si ho na bezpečné místo."
|
||||
}
|
||||
}
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Meetingdauer",
|
||||
"MeetingInterval": "Pause zwischen Meetings",
|
||||
"CalDavAccess": "CalDAV-Zugriff",
|
||||
"CalDavAccessPrompt": "Das CalDAV-Protokoll ermöglicht die Synchronisierung Ihres persönlichen Huly-Kalenders mit externen Kalenderanwendungen. Wählen Sie ein Konto aus und verwenden Sie das vorgeschlagene Passwort für die Registrierung des Huly-CalDAV-Servers in einer Kalender-Client-Anwendung.",
|
||||
"CalDavAccessPrompt": "Das CalDAV-Protokoll ermöglicht die Synchronisierung Ihres persönlichen Huly-Kalenders mit externen Kalenderanwendungen. Verwenden Sie dieses Konto und das vorgeschlagene Passwort, um den Huly CalDAV-Server in einer Kalender-Client-Anwendung zu registrieren.",
|
||||
"CalDavAccessEnable": "CalDAV-Zugriff auf den persönlichen Kalender im Arbeitsbereich {ws} aktivieren",
|
||||
"CalDavAccessServer": "CalDAV-Server",
|
||||
"CalDavAccessAccount": "Huly-Konto",
|
||||
"CalDavAccessPassword": "Passwort (automatisch generiert)"
|
||||
"CalDavAccessPassword": "Passwort",
|
||||
"CalDavAccessPasswordWarning": "Sie können dieses Passwort nicht erneut anzeigen. Bitte speichern Sie es an einem sicheren Ort."
|
||||
}
|
||||
}
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Meeting duration",
|
||||
"MeetingInterval": "Pause between meetings",
|
||||
"CalDavAccess": "CalDAV access",
|
||||
"CalDavAccessPrompt": "CalDAV protocol allows for syncing your personal Huly calendar with external calendar applications. Select an account and use the suggested password for registering the Huly CalDAV server in a calendar client application.",
|
||||
"CalDavAccessPrompt": "CalDAV protocol allows for syncing your personal Huly calendar with external calendar applications. Use this account and the suggested password for registering the Huly CalDAV server in a calendar client application.",
|
||||
"CalDavAccessEnable": "Enable CalDAV access to the personal calendar in workspace {ws}",
|
||||
"CalDavAccessServer": "CalDAV server",
|
||||
"CalDavAccessAccount": "Huly account",
|
||||
"CalDavAccessPassword": "Password (auto-generated)"
|
||||
"CalDavAccessPassword": "Password",
|
||||
"CalDavAccessPasswordWarning": "You will not be able to see this password again. Please save it in a secure place."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Duración de la reunión",
|
||||
"MeetingInterval": "Pausa entre reuniones",
|
||||
"CalDavAccess": "Acceso a CalDAV",
|
||||
"CalDavAccessPrompt": "El protocolo CalDAV permite sincronizar su calendario personal de Huly con aplicaciones de calendario externas. Seleccione una cuenta y use la contraseña sugerida para registrar el servidor CalDAV de Huly en una aplicación cliente de calendario.",
|
||||
"CalDavAccessPrompt": "El protocolo CalDAV permite sincronizar su calendario personal de Huly con aplicaciones de calendario externas. Utilice esta cuenta y la contraseña sugerida para registrar el servidor CalDAV de Huly en una aplicación cliente de calendario.",
|
||||
"CalDavAccessEnable": "Habilite el acceso CalDAV al calendario personal en el espacio de trabajo {ws}",
|
||||
"CalDavAccessServer": "Servidor CalDAV",
|
||||
"CalDavAccessAccount": "Cuenta de Huly",
|
||||
"CalDavAccessPassword": "Contraseña (autogenerada)"
|
||||
"CalDavAccessPassword": "Contraseña",
|
||||
"CalDavAccessPasswordWarning": "No podrá ver esta contraseña nuevamente. Guárdela en un lugar seguro."
|
||||
}
|
||||
}
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Durée de la réunion",
|
||||
"MeetingInterval": "Pause entre les réunions",
|
||||
"CalDavAccess": "Accès CalDAV",
|
||||
"CalDavAccessPrompt": "Le protocole CalDAV permet de synchroniser votre calendrier personnel Huly avec des applications de calendrier externes. Sélectionnez un compte et utilisez le mot de passe suggéré pour enregistrer le serveur CalDAV Huly dans une application cliente de calendrier.",
|
||||
"CalDavAccessPrompt": "Le protocole CalDAV permet de synchroniser votre calendrier personnel Huly avec des applications de calendrier externes. Utilisez ce compte et le mot de passe suggéré pour enregistrer le serveur CalDAV Huly dans une application cliente de calendrier.",
|
||||
"CalDavAccessEnable": "Activer l'accès CalDAV au calendrier personnel dans l'espace de travail {ws}",
|
||||
"CalDavAccessServer": "Serveur CalDAV",
|
||||
"CalDavAccessAccount": "Compte Huly",
|
||||
"CalDavAccessPassword": "Mot de passe (généré automatiquement)"
|
||||
"CalDavAccessPassword": "Mot de passe",
|
||||
"CalDavAccessPasswordWarning": "Vous ne pourrez plus voir ce mot de passe. Veuillez le conserver dans un endroit sûr."
|
||||
}
|
||||
}
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Durata riunione",
|
||||
"MeetingInterval": "Pausa tra riunioni",
|
||||
"CalDavAccess": "Accesso CalDAV",
|
||||
"CalDavAccessPrompt": "Il protocollo CalDAV consente la sincronizzazione del calendario personale Huly con applicazioni di calendario esterne. Seleziona un account e utilizza la password suggerita per registrare il server CalDAV Huly in un'applicazione client di calendario.",
|
||||
"CalDavAccessPrompt": "Il protocollo CalDAV consente la sincronizzazione del calendario personale Huly con applicazioni di calendario esterne. Utilizza questo account e la password suggerita per registrare il server CalDAV Huly in un'applicazione client di calendario.",
|
||||
"CalDavAccessEnable": "Abilita l'accesso CalDAV al calendario personale nell'area di lavoro {ws}",
|
||||
"CalDavAccessServer": "Server CalDAV",
|
||||
"CalDavAccessAccount": "Account Huly",
|
||||
"CalDavAccessPassword": "Password (generata automaticamente)"
|
||||
"CalDavAccessPassword": "Password",
|
||||
"CalDavAccessPasswordWarning": "Non potrai vedere di nuovo questa password. Conservala in un luogo sicuro."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Duração da reunião",
|
||||
"MeetingInterval": "Pausa entre reuniões",
|
||||
"CalDavAccess": "CalDAV Access",
|
||||
"CalDavAccessPrompt": "O protocolo CalDAV permite sincronizar o seu calendário pessoal Huly com aplicações de calendário externas. Selecione uma conta e utilize a palavra-passe sugerida para registar o servidor Huly CalDAV numa aplicação cliente de calendário.",
|
||||
"CalDavAccessPrompt": "O protocolo CalDAV permite sincronizar o seu calendário pessoal Huly com aplicações de calendário externas. Utilize esta conta e a palavra-passe sugerida para registar o servidor Huly CalDAV numa aplicação cliente de calendário.",
|
||||
"CalDavAccessEnable": "Ativar o acesso CalDAV ao calendário pessoal no espaço de trabalho {ws}",
|
||||
"CalDavAccessServer": "Servidor CalDAV",
|
||||
"CalDavAccessAccount": "Conta Huly",
|
||||
"CalDavAccessPassword": "Password (gerada automaticamente)"
|
||||
"CalDavAccessPassword": "Password",
|
||||
"CalDavAccessPasswordWarning": "Não será possível ver esta palavra-passe novamente. Guarde-a num local seguro."
|
||||
}
|
||||
}
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "Длительность встречи",
|
||||
"MeetingInterval": "Перерыв между встречами",
|
||||
"CalDavAccess": "Доступ через CalDAV",
|
||||
"CalDavAccessPrompt": "Протокол CalDAV позволяет синхронизировать ваш личный календарь в Huly со сторонними приложениями. Выберите учетную запись и используйте предложенный пароль для регистрации сервера Huly CalDAV в календарном приложении.",
|
||||
"CalDavAccessPrompt": "Протокол CalDAV позволяет синхронизировать ваш личный календарь Huly с внешними приложениями календаря. Используйте эту учетную запись и предложенный пароль для регистрации сервера Huly CalDAV в клиентском приложении календаря.",
|
||||
"CalDavAccessEnable": "Разрешить доступ к личному календарю в пространстве {ws} через CalDAV",
|
||||
"CalDavAccessServer": "Сервер CalDAV",
|
||||
"CalDavAccessAccount": "Учетная запись Huly",
|
||||
"CalDavAccessPassword": "Пароль (автоматический)"
|
||||
"CalDavAccessPassword": "Пароль",
|
||||
"CalDavAccessPasswordWarning": "Вы не сможете увидеть этот пароль снова. Пожалуйста, сохраните его в безопасном месте."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,10 +100,11 @@
|
||||
"MeetingDuration": "会议时长",
|
||||
"MeetingInterval": "会议间暂停",
|
||||
"CalDavAccess": "CalDAV 访问",
|
||||
"CalDavAccessPrompt": "CalDAV 协议允许将您的个人 Huly 日历与外部日历应用同步。请选择一个帐户并使用建议的密码在日历客户端应用中注册 Huly CalDav 服务器。",
|
||||
"CalDavAccessPrompt": "CalDAV 協定允許將您的個人 Huly 日曆與外部日曆應用程式同步。使用此帳戶和建議的密碼在日曆用戶端應用程式中註冊 Huly CalDAV 伺服器。",
|
||||
"CalDavAccessEnable": "在工作区 {ws} 中启用 CalDAV 对个人日历的访问",
|
||||
"CalDavAccessServer": "CalDAV 服务器",
|
||||
"CalDavAccessAccount": "Huly 帐户",
|
||||
"CalDavAccessPassword": "密码(自动生成)"
|
||||
"CalDavAccessPassword": "密码",
|
||||
"CalDavAccessPasswordWarning": "您将无法再次看到此密码。请将其保存在安全的地方。"
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { Integration, IntegrationKey } from '@hcengineering/account-client'
|
||||
import presentation, { getClient, getCurrentWorkspaceUrl, getCurrentWorkspaceUuid } from '@hcengineering/presentation'
|
||||
import {
|
||||
Dropdown,
|
||||
Label,
|
||||
ListItem,
|
||||
Modal,
|
||||
ModernEditbox,
|
||||
CheckBox,
|
||||
Spinner,
|
||||
Button,
|
||||
IconCopy,
|
||||
EditBox
|
||||
} from '@hcengineering/ui'
|
||||
import setting from '@hcengineering/setting'
|
||||
import { Integration } from '@hcengineering/account-client'
|
||||
import presentation, {
|
||||
copyTextToClipboard,
|
||||
getCurrentWorkspaceUrl,
|
||||
getCurrentWorkspaceUuid
|
||||
} from '@hcengineering/presentation'
|
||||
import { Label, Modal, CheckBox, Spinner, Button, IconCopy, EditBox } from '@hcengineering/ui'
|
||||
import calendar from '@hcengineering/calendar'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { getMetadata, IntlString, translateCB } from '@hcengineering/platform'
|
||||
import contact, { getCurrentEmployee, SocialIdentityRef } from '@hcengineering/contact'
|
||||
import { buildSocialIdString, getCurrentAccount, PersonId, SocialIdType } from '@hcengineering/core'
|
||||
import { calendarByIdStore, getAccountClient } from '../utils'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { getCurrentAccount, pickPrimarySocialId, SocialId, SocialIdType } from '@hcengineering/core'
|
||||
import { getAccountClient } from '../utils'
|
||||
|
||||
const workspaceUuid = getCurrentWorkspaceUuid()
|
||||
|
||||
@@ -28,8 +19,8 @@
|
||||
|
||||
let loading = true
|
||||
let error: string | undefined
|
||||
const availableAccounts: ListItem[] = []
|
||||
let selectedAccount: ListItem | undefined
|
||||
let selectedSocialId: SocialId | undefined
|
||||
let hulySocialId = ''
|
||||
let accessEnabled = false
|
||||
let serverUrl = getMetadata(calendar.metadata.CalDavServerURL)
|
||||
let password = ''
|
||||
@@ -40,9 +31,7 @@
|
||||
$: wasAccessEnabled = integrationGlobal !== null && integrationWs !== null
|
||||
$: canSave = !!(
|
||||
!loading &&
|
||||
((!wasAccessEnabled && accessEnabled && selectedAccount != null) ||
|
||||
(wasAccessEnabled && !accessEnabled) ||
|
||||
(wasAccessEnabled && accessEnabled && selectedAccount != null && selectedAccount._id !== integrationWs?.socialId))
|
||||
((!wasAccessEnabled && accessEnabled && selectedSocialId != null) || (wasAccessEnabled && !accessEnabled))
|
||||
)
|
||||
|
||||
function generateRandomPassword (length = 24): string {
|
||||
@@ -60,70 +49,69 @@
|
||||
try {
|
||||
password = generateRandomPassword()
|
||||
|
||||
const account = getCurrentAccount()
|
||||
const accountClient = getAccountClient()
|
||||
const socialId = pickPrimarySocialId(getCurrentAccount().fullSocialIds)
|
||||
if (socialId.type !== SocialIdType.HULY) {
|
||||
// Thid should not happen, no need to translate
|
||||
error = 'Appropriate account not found'
|
||||
return
|
||||
}
|
||||
|
||||
const wsId = getCurrentWorkspaceUuid()
|
||||
for (const socialId of account.fullSocialIds) {
|
||||
// TODO: Handle delete social ids generically, we don't have isDelete field on thr workspace side
|
||||
if (socialId.type === SocialIdType.EMAIL && socialId.value.includes('#')) {
|
||||
continue
|
||||
}
|
||||
if (availableAccounts.find((a) => a.label === socialId.value) == null) {
|
||||
availableAccounts.push({
|
||||
_id: socialId._id,
|
||||
label: socialId.value
|
||||
})
|
||||
}
|
||||
const integrations = await accountClient.listIntegrations({
|
||||
socialId: socialId._id,
|
||||
kind: 'caldav'
|
||||
})
|
||||
for (const integration of integrations) {
|
||||
if (integration.workspaceUuid == null) {
|
||||
if (integrationGlobal == null) {
|
||||
integrationGlobal = integration
|
||||
continue
|
||||
}
|
||||
}
|
||||
totalWsIntegrations += 1
|
||||
if (integration.workspaceUuid === wsId && integrationWs == null) {
|
||||
integrationWs = integration
|
||||
selectedAccount = {
|
||||
_id: socialId._id,
|
||||
label: socialId.value
|
||||
}
|
||||
hulySocialId = socialId.value
|
||||
const accountClient = getAccountClient()
|
||||
const integrations = await accountClient.listIntegrations({
|
||||
socialId: socialId._id,
|
||||
kind: 'caldav'
|
||||
})
|
||||
for (const integration of integrations) {
|
||||
if (integration.workspaceUuid == null) {
|
||||
if (integrationGlobal == null) {
|
||||
integrationGlobal = integration
|
||||
continue
|
||||
}
|
||||
}
|
||||
totalWsIntegrations += 1
|
||||
if (integration.workspaceUuid === wsId && integrationWs == null) {
|
||||
integrationWs = integration
|
||||
}
|
||||
}
|
||||
selectedSocialId = socialId
|
||||
accessEnabled = integrationWs != null && integrationGlobal != null
|
||||
} catch (err: any) {
|
||||
console.error('Failed to load CalDAV intergrations', err)
|
||||
error = `${err.message ?? 'Unable to load CalDAV intergrations'}`
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
loading = false
|
||||
})
|
||||
|
||||
async function save (): Promise<void> {
|
||||
loading = true
|
||||
try {
|
||||
if (selectedSocialId == null) {
|
||||
return
|
||||
}
|
||||
const accountClient = getAccountClient()
|
||||
const socialId = selectedSocialId._id
|
||||
const kind = 'caldav'
|
||||
const key = 'password'
|
||||
if (wasAccessEnabled && !accessEnabled) {
|
||||
// Access disabled
|
||||
await accountClient.deleteIntegration({
|
||||
socialId: selectedAccount?._id as PersonId,
|
||||
kind: 'caldav',
|
||||
socialId,
|
||||
kind,
|
||||
workspaceUuid
|
||||
})
|
||||
if (totalWsIntegrations === 1 && integrationGlobal != null) {
|
||||
await accountClient.deleteIntegrationSecret({
|
||||
socialId: integrationGlobal.socialId,
|
||||
kind: 'caldav',
|
||||
socialId,
|
||||
kind,
|
||||
workspaceUuid: null,
|
||||
key: 'caldav'
|
||||
key
|
||||
})
|
||||
await accountClient.deleteIntegration({
|
||||
socialId: integrationGlobal.socialId,
|
||||
kind: 'caldav',
|
||||
socialId,
|
||||
kind,
|
||||
workspaceUuid: null
|
||||
})
|
||||
}
|
||||
@@ -131,46 +119,21 @@
|
||||
// Access enabled
|
||||
if (integrationGlobal === null) {
|
||||
await accountClient.createIntegration({
|
||||
socialId: selectedAccount?._id as PersonId,
|
||||
kind: 'caldav',
|
||||
workspaceUuid: null
|
||||
})
|
||||
integrationGlobal = await accountClient.getIntegration({
|
||||
socialId: selectedAccount?._id as PersonId,
|
||||
kind: 'caldav',
|
||||
socialId,
|
||||
kind,
|
||||
workspaceUuid: null
|
||||
})
|
||||
}
|
||||
const secret = await accountClient.getIntegrationSecret({
|
||||
socialId: integrationGlobal?.socialId as PersonId,
|
||||
kind: 'caldav',
|
||||
await accountClient.addIntegrationSecret({
|
||||
socialId,
|
||||
kind,
|
||||
workspaceUuid: null,
|
||||
key: 'caldav'
|
||||
})
|
||||
if (secret == null) {
|
||||
await accountClient.addIntegrationSecret({
|
||||
socialId: integrationGlobal?.socialId as PersonId,
|
||||
kind: 'caldav',
|
||||
workspaceUuid: null,
|
||||
key: 'caldav',
|
||||
secret: password
|
||||
})
|
||||
}
|
||||
await accountClient.createIntegration({
|
||||
socialId: selectedAccount?._id as PersonId,
|
||||
kind: 'caldav',
|
||||
workspaceUuid
|
||||
})
|
||||
} else if (accessEnabled && wasAccessEnabled && selectedAccount?._id !== integrationWs?.socialId) {
|
||||
// Access changed
|
||||
await accountClient.deleteIntegration({
|
||||
socialId: integrationWs?.socialId as PersonId,
|
||||
kind: 'caldav',
|
||||
workspaceUuid
|
||||
key,
|
||||
secret: password
|
||||
})
|
||||
await accountClient.createIntegration({
|
||||
socialId: selectedAccount?._id as PersonId,
|
||||
kind: 'caldav',
|
||||
socialId,
|
||||
kind,
|
||||
workspaceUuid
|
||||
})
|
||||
}
|
||||
@@ -178,42 +141,21 @@
|
||||
} catch (err: any) {
|
||||
error = `${err.message ?? 'Unable to save CalDAV intergration'}`
|
||||
console.error('Failed to save CalDAV integration', err)
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
|
||||
async function copyServer (): Promise<void> {
|
||||
console.log(serverUrl)
|
||||
await navigator?.clipboard?.writeText(serverUrl ?? '')
|
||||
await copyTextToClipboard(serverUrl ?? '')
|
||||
}
|
||||
|
||||
async function copyAccount (): Promise<void> {
|
||||
console.log(selectedAccount?.label)
|
||||
await navigator?.clipboard?.writeText(selectedAccount?.label ?? '')
|
||||
await copyTextToClipboard(selectedSocialId?.value ?? '')
|
||||
}
|
||||
|
||||
async function copyPassword (): Promise<void> {
|
||||
loading = true
|
||||
try {
|
||||
if (integrationGlobal != null) {
|
||||
const accountClient = getAccountClient()
|
||||
const secret = await accountClient.getIntegrationSecret({
|
||||
socialId: integrationGlobal.socialId,
|
||||
kind: 'caldav',
|
||||
workspaceUuid: null,
|
||||
key: 'caldav'
|
||||
})
|
||||
if (secret != null) {
|
||||
await navigator?.clipboard?.writeText(secret.secret)
|
||||
return
|
||||
}
|
||||
}
|
||||
await navigator?.clipboard?.writeText(password)
|
||||
} catch (err: any) {
|
||||
error = `${err.message ?? 'Unable to copy password'}`
|
||||
console.error('Failed to copy password', err)
|
||||
}
|
||||
loading = false
|
||||
await copyTextToClipboard(password)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -267,19 +209,7 @@
|
||||
<div class="flex-col-stretch flex-gap-1-5" class:accessDisabled={!accessEnabled}>
|
||||
<Label label={calendar.string.CalDavAccessAccount} />
|
||||
<div class="flex-row-center flex-gap-1">
|
||||
<Dropdown
|
||||
size="large"
|
||||
placeholder={calendar.string.CalDavAccessAccount}
|
||||
items={availableAccounts}
|
||||
selected={selectedAccount}
|
||||
justify="left"
|
||||
withSearch={false}
|
||||
stretchWidth={true}
|
||||
disabled={!accessEnabled}
|
||||
on:selected={(e) => {
|
||||
selectedAccount = e.detail
|
||||
}}
|
||||
/>
|
||||
<EditBox bind:value={hulySocialId} kind="ghost" fullSize focusable disabled={true} />
|
||||
<Button
|
||||
icon={IconCopy}
|
||||
size="small"
|
||||
@@ -291,20 +221,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-col-stretch flex-gap-1-5" class:accessDisabled={!accessEnabled}>
|
||||
<Label label={calendar.string.CalDavAccessPassword} />
|
||||
<div class="flex-row-center flex-gap-1">
|
||||
<EditBox bind:value={password} kind="ghost" format="password" fullSize focusable disabled={true} />
|
||||
<Button
|
||||
icon={IconCopy}
|
||||
size="small"
|
||||
kind="ghost"
|
||||
disabled={!accessEnabled}
|
||||
showTooltip={{ label: presentation.string.Copy }}
|
||||
on:click={copyPassword}
|
||||
/>
|
||||
{#if !wasAccessEnabled}
|
||||
<div class="flex-col-stretch flex-gap-1-5" class:accessDisabled={!accessEnabled}>
|
||||
<Label label={calendar.string.CalDavAccessPassword} />
|
||||
<div class="flex-row-center flex-gap-1">
|
||||
<EditBox bind:value={password} kind="ghost" format="password" fullSize focusable disabled={true} />
|
||||
<Button
|
||||
icon={IconCopy}
|
||||
size="small"
|
||||
kind="ghost"
|
||||
disabled={!accessEnabled}
|
||||
showTooltip={{ label: presentation.string.Copy }}
|
||||
on:click={copyPassword}
|
||||
/>
|
||||
</div>
|
||||
{#if canSave}
|
||||
<Label label={calendar.string.CalDavAccessPasswordWarning} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if error}
|
||||
<div style="color: var(--theme-error-color)">{error}</div>
|
||||
|
||||
@@ -214,9 +214,6 @@
|
||||
readonly={readOnly}
|
||||
placeholder={calendar.string.Description}
|
||||
bind:content={description}
|
||||
on:changeSize={(e) => {
|
||||
console.log('ChangeSize', e)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="divider" />
|
||||
|
||||
@@ -246,7 +246,7 @@ const calendarPlugin = plugin(calendarId, {
|
||||
CalDavAccessServer: '' as IntlString,
|
||||
CalDavAccessAccount: '' as IntlString,
|
||||
CalDavAccessPassword: '' as IntlString,
|
||||
CopyValue: '' as IntlString
|
||||
CalDavAccessPasswordWarning: '' as IntlString
|
||||
},
|
||||
handler: {
|
||||
DisconnectHandler: '' as Handler
|
||||
|
||||
@@ -133,7 +133,6 @@ export function followeeDataUnsubscribe (topic: string, handler: (data: any) =>
|
||||
}
|
||||
|
||||
export function toggleFollowee (person: Ref<Person> | undefined): void {
|
||||
console.log('toggle followee', person)
|
||||
followee.update((p) => (p === person ? undefined : person))
|
||||
|
||||
const f = get(followee)
|
||||
@@ -150,7 +149,6 @@ export function toggleFollowee (person: Ref<Person> | undefined): void {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('no followee')
|
||||
for (const handlers of followeeDataHandlers.values()) {
|
||||
for (const handler of handlers) {
|
||||
handler(undefined)
|
||||
|
||||
@@ -1248,7 +1248,6 @@ describe('integration methods', () => {
|
||||
})
|
||||
|
||||
describe('getIntegrationSecret', () => {
|
||||
const mockAccount = 'test-account'
|
||||
const mockSocialId = 'test-social-id' as PersonId
|
||||
const mockSecretKey: IntegrationSecretKey = {
|
||||
socialId: mockSocialId,
|
||||
@@ -1267,56 +1266,11 @@ describe('integration methods', () => {
|
||||
extra: { service: 'github' }
|
||||
})
|
||||
;(mockDb.integrationSecret.findOne as jest.Mock).mockResolvedValue(mockSecret)
|
||||
;(mockDb.socialId.findOne as jest.Mock).mockResolvedValue({ _id: mockSocialId })
|
||||
;(mockDb.workspace.findOne as jest.Mock).mockResolvedValue({ uuid: 'test-workspace' })
|
||||
;(mockDb.integration.findOne as jest.Mock).mockResolvedValue({})
|
||||
|
||||
const result = await getIntegrationSecret(mockCtx, mockDb, mockBranding, mockToken, mockSecretKey)
|
||||
|
||||
expect(result).toEqual(mockSecret)
|
||||
expect(mockDb.integrationSecret.findOne).toHaveBeenCalledWith(mockSecretKey)
|
||||
expect(mockDb.socialId.findOne).toHaveBeenCalledWith({ _id: mockSocialId, verifiedOn: { $gt: 0 } })
|
||||
})
|
||||
|
||||
test('should allow verified user to get their secret', async () => {
|
||||
;(decodeTokenVerbose as jest.Mock).mockReturnValue({
|
||||
account: mockAccount
|
||||
})
|
||||
;(mockDb.integrationSecret.findOne as jest.Mock).mockResolvedValue(mockSecret)
|
||||
;(mockDb.socialId.findOne as jest.Mock).mockResolvedValue({ _id: mockSocialId })
|
||||
;(mockDb.workspace.findOne as jest.Mock).mockResolvedValue({ uuid: 'test-workspace' })
|
||||
;(mockDb.integration.findOne as jest.Mock).mockResolvedValue({})
|
||||
|
||||
const result = await getIntegrationSecret(mockCtx, mockDb, mockBranding, mockToken, mockSecretKey)
|
||||
|
||||
expect(result).toEqual(mockSecret)
|
||||
expect(mockDb.integrationSecret.findOne).toHaveBeenCalledWith(mockSecretKey)
|
||||
expect(mockDb.socialId.findOne).toHaveBeenCalledWith({
|
||||
_id: mockSocialId,
|
||||
personUuid: mockAccount,
|
||||
verifiedOn: { $gt: 0 }
|
||||
})
|
||||
})
|
||||
|
||||
test('should throw error when user gets secret for different account', async () => {
|
||||
;(decodeTokenVerbose as jest.Mock).mockReturnValue({
|
||||
account: mockAccount
|
||||
})
|
||||
;(mockDb.integrationSecret.findOne as jest.Mock).mockResolvedValue(mockSecret)
|
||||
;(mockDb.socialId.findOne as jest.Mock).mockResolvedValue(null)
|
||||
;(mockDb.workspace.findOne as jest.Mock).mockResolvedValue({ uuid: 'test-workspace' })
|
||||
;(mockDb.integration.findOne as jest.Mock).mockResolvedValue({})
|
||||
|
||||
await expect(getIntegrationSecret(mockCtx, mockDb, mockBranding, mockToken, mockSecretKey)).rejects.toThrow(
|
||||
new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
||||
)
|
||||
|
||||
expect(mockDb.integrationSecret.findOne).not.toHaveBeenCalledWith()
|
||||
expect(mockDb.socialId.findOne).toHaveBeenCalledWith({
|
||||
_id: mockSocialId,
|
||||
personUuid: mockAccount,
|
||||
verifiedOn: { $gt: 0 }
|
||||
})
|
||||
})
|
||||
|
||||
test('should return null when integration secret not found', async () => {
|
||||
|
||||
@@ -1187,9 +1187,10 @@ export async function getWorkspacesInfo (
|
||||
db: AccountDB,
|
||||
branding: Branding | null,
|
||||
token: string,
|
||||
ids: WorkspaceUuid[]
|
||||
params: { ids: WorkspaceUuid[] }
|
||||
): Promise<WorkspaceInfoWithStatus[]> {
|
||||
const { account } = decodeTokenVerbose(ctx, token)
|
||||
const { ids } = params
|
||||
|
||||
if (account !== systemAccountUuid) {
|
||||
ctx.error('getWorkspaceInfos with wrong user', { account, token })
|
||||
|
||||
@@ -701,12 +701,16 @@ export async function addIntegrationSecret (
|
||||
params: IntegrationSecret
|
||||
): Promise<void> {
|
||||
const { extra, account } = decodeTokenVerbose(ctx, token)
|
||||
const { socialId, kind, workspaceUuid, key, secret } = params
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existingIntegration = await findExistingIntegration(account, db, params, extra)
|
||||
if (existingIntegration == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationNotFound, {}))
|
||||
}
|
||||
|
||||
const { socialId, kind, workspaceUuid, key, secret } = params
|
||||
const secretKey: IntegrationSecretKey = { socialId, kind, workspaceUuid, key }
|
||||
const existingSecret = await db.integrationSecret.findOne(secretKey)
|
||||
if (existingSecret != null) {
|
||||
@@ -724,12 +728,16 @@ export async function updateIntegrationSecret (
|
||||
params: IntegrationSecret
|
||||
): Promise<void> {
|
||||
const { extra, account } = decodeTokenVerbose(ctx, token)
|
||||
const { socialId, kind, workspaceUuid, key, secret } = params
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existingIntegration = await findExistingIntegration(account, db, params, extra)
|
||||
if (existingIntegration == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationNotFound, {}))
|
||||
}
|
||||
|
||||
const { socialId, kind, workspaceUuid, key, secret } = params
|
||||
const secretKey: IntegrationSecretKey = { socialId, kind, workspaceUuid, key }
|
||||
const existingSecret = await db.integrationSecret.findOne(secretKey)
|
||||
if (existingSecret == null) {
|
||||
@@ -747,12 +755,16 @@ export async function deleteIntegrationSecret (
|
||||
params: IntegrationSecretKey
|
||||
): Promise<void> {
|
||||
const { extra, account } = decodeTokenVerbose(ctx, token)
|
||||
const { socialId, kind, workspaceUuid, key } = params
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
|
||||
const existingIntegration = await findExistingIntegration(account, db, params, extra)
|
||||
if (existingIntegration == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.IntegrationNotFound, {}))
|
||||
}
|
||||
|
||||
const { socialId, kind, workspaceUuid, key } = params
|
||||
const secretKey: IntegrationSecretKey = { socialId, kind, workspaceUuid, key }
|
||||
const existingSecret = await db.integrationSecret.findOne(secretKey)
|
||||
if (existingSecret == null) {
|
||||
@@ -769,13 +781,13 @@ export async function getIntegrationSecret (
|
||||
token: string,
|
||||
params: IntegrationSecretKey
|
||||
): Promise<IntegrationSecret | null> {
|
||||
const { extra, account } = decodeTokenVerbose(ctx, token)
|
||||
const existingIntegration = await findExistingIntegration(account, db, params, extra)
|
||||
if (existingIntegration == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { extra } = decodeTokenVerbose(ctx, token)
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid, key } = params
|
||||
|
||||
if (kind == null || socialId == null || workspaceUuid === undefined || key == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
|
||||
}
|
||||
const existing = await db.integrationSecret.findOne({ socialId, kind, workspaceUuid, key })
|
||||
|
||||
return existing
|
||||
@@ -788,20 +800,10 @@ export async function listIntegrationsSecrets (
|
||||
token: string,
|
||||
params: Partial<IntegrationSecretKey>
|
||||
): Promise<IntegrationSecret[]> {
|
||||
const { extra, account } = decodeTokenVerbose(ctx, token)
|
||||
const { extra } = decodeTokenVerbose(ctx, token)
|
||||
verifyAllowedServices(integrationServices, extra)
|
||||
const { socialId, kind, workspaceUuid, key } = params
|
||||
|
||||
if (!verifyAllowedServices(integrationServices, extra, false)) {
|
||||
if (socialId != null) {
|
||||
const existingSocialId = await db.socialId.findOne({ _id: socialId, personUuid: account, verifiedOn: { $gt: 0 } })
|
||||
if (existingSocialId == null) {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
||||
}
|
||||
} else {
|
||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
||||
}
|
||||
}
|
||||
|
||||
return await db.integrationSecret.find({ socialId, kind, workspaceUuid, key })
|
||||
}
|
||||
|
||||
|
||||
@@ -1439,7 +1439,7 @@ export async function setTimezoneIfNotDefined (
|
||||
}
|
||||
|
||||
// Move to config?
|
||||
export const integrationServices = ['github', 'telegram-bot', 'telegram', 'mailbox']
|
||||
export const integrationServices = ['github', 'telegram-bot', 'telegram', 'mailbox', 'caldav']
|
||||
|
||||
export async function findExistingIntegration (
|
||||
account: AccountUuid,
|
||||
|
||||
Reference in New Issue
Block a user