cozy-sharing 37.0.2 → 37.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/OpenSharingLinkButton.js +1 -1
- package/dist/SharingProvider.js +315 -138
- package/dist/SharingProvider.spec.js +454 -240
- package/dist/actions/addToCozySharingLink.js +1 -1
- package/dist/actions/createCozySharingLink.js +1 -1
- package/dist/actions/shareNative.js +1 -1
- package/dist/actions/syncToCozySharingLink.js +1 -1
- package/dist/components/Avatar/AvatarList.js +1 -1
- package/dist/components/Recipient/GroupRecipient.js +0 -2
- package/dist/components/Recipient/GroupRecipientDetail.js +2 -25
- package/dist/components/Recipient/GroupRecipientDetailWithoutAccess.js +1 -1
- package/dist/components/Recipient/LinkRecipient.js +1 -1
- package/dist/components/Recipient/LinkRecipientLite.js +1 -1
- package/dist/components/Recipient/actions/revokeLink.js +1 -1
- package/dist/components/ShareAutosuggest.js +6 -6
- package/dist/components/ShareButton.js +1 -1
- package/dist/components/ShareByLink.js +1 -1
- package/dist/components/ShareLinkAccessModal/ShareLinkAccessModal.js +316 -0
- package/dist/components/ShareLinkAccessModal/ShareLinkAccessModal.spec.js +372 -0
- package/dist/components/ShareLinkAccessModal/index.js +1 -0
- package/dist/components/ShareRestrictionModal/BoxDate.js +1 -1
- package/dist/components/ShareRestrictionModal/BoxEditingRights.js +1 -1
- package/dist/components/ShareRestrictionModal/BoxPassword.js +1 -1
- package/dist/components/ShareRestrictionModal/ShareRestrictionModal.js +1 -1
- package/dist/components/ShareRestrictionModal/helpers.js +36 -18
- package/dist/components/ShareRestrictionModal/helpers.spec.js +3 -3
- package/dist/components/SharedBadge.js +1 -1
- package/dist/components/SharedDrive/SharedDriveHeader.js +1 -1
- package/dist/index.js +1 -0
- package/locales/en.json +21 -3
- package/locales/fr.json +22 -4
- package/locales/ru.json +21 -3
- package/locales/vi.json +21 -3
- package/package.json +3 -3
- package/src/OpenSharingLinkButton.jsx +1 -1
- package/src/SharingProvider.jsx +91 -27
- package/src/SharingProvider.spec.jsx +212 -60
- package/src/actions/addToCozySharingLink.js +1 -1
- package/src/actions/createCozySharingLink.js +1 -1
- package/src/actions/shareNative.js +1 -1
- package/src/actions/syncToCozySharingLink.js +1 -1
- package/src/components/Avatar/AvatarList.jsx +1 -1
- package/src/components/Recipient/GroupRecipient.jsx +1 -2
- package/src/components/Recipient/GroupRecipientDetail.jsx +3 -33
- package/src/components/Recipient/GroupRecipientDetailWithoutAccess.jsx +1 -1
- package/src/components/Recipient/LinkRecipient.jsx +1 -1
- package/src/components/Recipient/LinkRecipientLite.jsx +1 -1
- package/src/components/Recipient/actions/revokeLink.js +1 -1
- package/src/components/ShareAutosuggest.jsx +3 -4
- package/src/components/ShareButton.jsx +1 -1
- package/src/components/ShareByLink.jsx +1 -1
- package/src/components/ShareLinkAccessModal/ShareLinkAccessModal.jsx +293 -0
- package/src/components/ShareLinkAccessModal/ShareLinkAccessModal.spec.jsx +291 -0
- package/src/components/ShareLinkAccessModal/index.js +1 -0
- package/src/components/ShareRestrictionModal/BoxDate.jsx +1 -1
- package/src/components/ShareRestrictionModal/BoxEditingRights.jsx +1 -1
- package/src/components/ShareRestrictionModal/BoxPassword.jsx +1 -1
- package/src/components/ShareRestrictionModal/ShareRestrictionModal.jsx +1 -1
- package/src/components/ShareRestrictionModal/helpers.js +30 -6
- package/src/components/ShareRestrictionModal/helpers.spec.js +3 -3
- package/src/components/SharedBadge.jsx +2 -1
- package/src/components/SharedDrive/SharedDriveHeader.jsx +1 -1
- package/src/helpers/sharings.js +0 -1
- package/src/index.jsx +1 -0
package/locales/ru.json
CHANGED
|
@@ -391,10 +391,8 @@
|
|
|
391
391
|
"secondary_you": "включая вас"
|
|
392
392
|
},
|
|
393
393
|
"GroupRecipientDetail": {
|
|
394
|
-
"subtitle": "Управляется %{ownerName}",
|
|
395
394
|
"empty": {
|
|
396
|
-
"content": "
|
|
397
|
-
"action": "Открыть Контакты"
|
|
395
|
+
"content": "Эта группа пуста. Чтобы добавить участников в эту группу, обратитесь к администратору."
|
|
398
396
|
},
|
|
399
397
|
"withAccess": {
|
|
400
398
|
"title": "Доступ к элементу"
|
|
@@ -462,5 +460,25 @@
|
|
|
462
460
|
"successNotification": "Командный диск был изменён",
|
|
463
461
|
"errorNotification": "Ошибка при модификации командного диска"
|
|
464
462
|
}
|
|
463
|
+
},
|
|
464
|
+
"ShareLinkAccessModal": {
|
|
465
|
+
"title": "Настроить доступ по ссылке",
|
|
466
|
+
"introText": "Выберите, что получатели могут делать с этими файлами.",
|
|
467
|
+
"anyoneWithLink": "Разрешить доступ всем, у кого есть ссылка",
|
|
468
|
+
"settings": "Настройки доступа",
|
|
469
|
+
"accessLevel": "Уровень доступа",
|
|
470
|
+
"viewer": "Просмотр",
|
|
471
|
+
"editor": "Редактирование",
|
|
472
|
+
"settingsTitle": "Настройки доступа",
|
|
473
|
+
"expiry": "Установить срок действия",
|
|
474
|
+
"expiryDate": "Дата истечения",
|
|
475
|
+
"password": "Требовать пароль",
|
|
476
|
+
"passwordLabel": "Пароль",
|
|
477
|
+
"passwordTooShort": "Пароль должен содержать не менее %{count} символов.",
|
|
478
|
+
"cancel": "Отмена",
|
|
479
|
+
"addLinks": "Добавить ссылки",
|
|
480
|
+
"error": {
|
|
481
|
+
"persistence": "Не удалось сохранить разрешения ссылки. Попробуйте еще раз."
|
|
482
|
+
}
|
|
465
483
|
}
|
|
466
484
|
}
|
package/locales/vi.json
CHANGED
|
@@ -391,10 +391,8 @@
|
|
|
391
391
|
"secondary_you": "bao gồm cả bạn"
|
|
392
392
|
},
|
|
393
393
|
"GroupRecipientDetail": {
|
|
394
|
-
"subtitle": "Được quản lý bởi %{ownerName}",
|
|
395
394
|
"empty": {
|
|
396
|
-
"content": "Nhóm
|
|
397
|
-
"action": "Mở Danh bạ"
|
|
395
|
+
"content": "Nhóm này hiện không có thành viên nào. Để thêm thành viên vào nhóm này, vui lòng liên hệ với quản trị viên."
|
|
398
396
|
},
|
|
399
397
|
"withAccess": {
|
|
400
398
|
"title": "Quyền truy cập vào mục"
|
|
@@ -462,5 +460,25 @@
|
|
|
462
460
|
"successNotification": "Ổ đĩa nhóm đã được sửa đổi",
|
|
463
461
|
"errorNotification": "Lỗi khi sửa đổi ổ đĩa nhóm"
|
|
464
462
|
}
|
|
463
|
+
},
|
|
464
|
+
"ShareLinkAccessModal": {
|
|
465
|
+
"title": "Cài đặt quyền truy cập liên kết",
|
|
466
|
+
"introText": "Chọn những gì người nhận có thể làm với các tệp này.",
|
|
467
|
+
"anyoneWithLink": "Cho phép bất kỳ ai có liên kết truy cập",
|
|
468
|
+
"settings": "Cài đặt chia sẻ",
|
|
469
|
+
"accessLevel": "Cấp độ truy cập",
|
|
470
|
+
"viewer": "Người xem",
|
|
471
|
+
"editor": "Người chỉnh sửa",
|
|
472
|
+
"settingsTitle": "Cài đặt chia sẻ",
|
|
473
|
+
"expiry": "Đặt ngày hết hạn",
|
|
474
|
+
"expiryDate": "Ngày hết hạn",
|
|
475
|
+
"password": "Yêu cầu mật khẩu",
|
|
476
|
+
"passwordLabel": "Mật khẩu",
|
|
477
|
+
"passwordTooShort": "Mật khẩu phải có ít nhất %{count} ký tự.",
|
|
478
|
+
"cancel": "Hủy",
|
|
479
|
+
"addLinks": "Thêm liên kết",
|
|
480
|
+
"error": {
|
|
481
|
+
"persistence": "Không thể lưu quyền của liên kết. Vui lòng thử lại."
|
|
482
|
+
}
|
|
465
483
|
}
|
|
466
484
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "37.0
|
|
3
|
+
"version": "37.2.0",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"cozy-intent": "^2.31.1",
|
|
60
60
|
"cozy-minilog": "^3.10.1",
|
|
61
61
|
"cozy-ui": "^140.5.0",
|
|
62
|
-
"cozy-ui-plus": "^12.1.
|
|
62
|
+
"cozy-ui-plus": "^12.1.2",
|
|
63
63
|
"jest": "30.3.0",
|
|
64
64
|
"jest-environment-jsdom": "30.3.0",
|
|
65
65
|
"react": "16.12.0",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"sideEffects": [
|
|
86
86
|
"*.css"
|
|
87
87
|
],
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "0cb65d680568b4cec09ebc03f324878cdcc7242c"
|
|
89
89
|
}
|
package/src/SharingProvider.jsx
CHANGED
|
@@ -3,7 +3,10 @@ import React, { Component } from 'react'
|
|
|
3
3
|
import { withClient } from 'cozy-client'
|
|
4
4
|
import minilog from 'cozy-minilog'
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
generateShareLinkFromFile,
|
|
8
|
+
getLinkAccessOptions
|
|
9
|
+
} from './components/ShareRestrictionModal/helpers'
|
|
7
10
|
import SharingContext from './context'
|
|
8
11
|
import { fetchNextPermissions } from './fetchNextPermissions'
|
|
9
12
|
import { fetchFilesPaths } from './helpers/files'
|
|
@@ -41,6 +44,7 @@ import reducer, {
|
|
|
41
44
|
getSharedDocIdsBySharings,
|
|
42
45
|
getDocumentSharing,
|
|
43
46
|
getDocumentPermissions,
|
|
47
|
+
getPermissionDocIds,
|
|
44
48
|
hasSharedParent,
|
|
45
49
|
hasSharedChild,
|
|
46
50
|
getSharedParentPath,
|
|
@@ -52,6 +56,8 @@ const log = minilog('SharingProvider')
|
|
|
52
56
|
const SHARING_DOCTYPE = 'io.cozy.sharings'
|
|
53
57
|
const PERMISSION_DOCTYPE = 'io.cozy.permissions'
|
|
54
58
|
|
|
59
|
+
const getDocumentId = document => document?._id || document?.id || null
|
|
60
|
+
|
|
55
61
|
export class SharingProvider extends Component {
|
|
56
62
|
constructor(props, context) {
|
|
57
63
|
super(props, context)
|
|
@@ -90,6 +96,7 @@ export class SharingProvider extends Component {
|
|
|
90
96
|
revokeGroup: this.revokeGroup,
|
|
91
97
|
revokeSelf: this.revokeSelf,
|
|
92
98
|
shareByLink: this.shareByLink,
|
|
99
|
+
ensureSharingLink: this.ensureSharingLink,
|
|
93
100
|
getFederatedShareLink: this.getFederatedShareLink,
|
|
94
101
|
fetchSharedDriveSharingLinks: this.fetchSharedDriveSharingLinks,
|
|
95
102
|
updateDocumentPermissions: this.updateDocumentPermissions,
|
|
@@ -430,10 +437,76 @@ export class SharingProvider extends Component {
|
|
|
430
437
|
}
|
|
431
438
|
}
|
|
432
439
|
|
|
440
|
+
fetchSharingLinks = async (document, permissionCol = this.permissionCol) => {
|
|
441
|
+
const documentId = getDocumentId(document)
|
|
442
|
+
if (!documentId) return []
|
|
443
|
+
|
|
444
|
+
const response = document.driveId
|
|
445
|
+
? await permissionCol.findLinksByIds([documentId])
|
|
446
|
+
: await permissionCol.findLinksByDoctype(this.props.doctype)
|
|
447
|
+
const permissions = (response?.data || []).filter(
|
|
448
|
+
permission =>
|
|
449
|
+
document.driveId || getPermissionDocIds(permission).includes(documentId)
|
|
450
|
+
)
|
|
451
|
+
const existingPermissions = getDocumentPermissions(this.state, documentId)
|
|
452
|
+
const existingPermissionIds = existingPermissions.map(
|
|
453
|
+
permission => permission.id
|
|
454
|
+
)
|
|
455
|
+
const newPermissions = permissions.filter(
|
|
456
|
+
permission => !existingPermissionIds.includes(permission.id)
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
if (newPermissions.length > 0) {
|
|
460
|
+
this.dispatch(addSharingLink(newPermissions))
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return permissions
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
ensureSharingLink = async (document, linkAccess) => {
|
|
467
|
+
const documentId = getDocumentId(document)
|
|
468
|
+
if (!documentId) {
|
|
469
|
+
throw new Error('Sharing links require a document id')
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const permissions = document.driveId
|
|
473
|
+
? await this.fetchSharedDriveSharingLinks(document)
|
|
474
|
+
: await this.fetchSharingLinks(document)
|
|
475
|
+
const { verbs, expiresAt, ttl, password } = getLinkAccessOptions(linkAccess)
|
|
476
|
+
const permissionResponse =
|
|
477
|
+
permissions.length > 0
|
|
478
|
+
? (
|
|
479
|
+
await this.updateDocumentPermissions(
|
|
480
|
+
{ ...document, id: documentId },
|
|
481
|
+
{ verbs, expiresAt, password },
|
|
482
|
+
permissions
|
|
483
|
+
)
|
|
484
|
+
)[0]
|
|
485
|
+
: await this.shareByLink(document, { verbs, ttl, password })
|
|
486
|
+
const permission = permissionResponse?.data
|
|
487
|
+
const sharecode = getShortcode(permission)
|
|
488
|
+
|
|
489
|
+
if (!sharecode) {
|
|
490
|
+
throw new Error('Sharing permission does not contain a shortcode')
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
documentId,
|
|
495
|
+
url: generateShareLinkFromFile({
|
|
496
|
+
client: this.props.client,
|
|
497
|
+
file: document,
|
|
498
|
+
sharecode,
|
|
499
|
+
getOwner: this.state.getOwner,
|
|
500
|
+
getSharingById: this.state.getSharingById,
|
|
501
|
+
documentType: this.state.documentType
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
433
506
|
getFederatedShareLink = document => {
|
|
434
507
|
if (!document?.driveId) return null
|
|
435
508
|
|
|
436
|
-
const documentId = document
|
|
509
|
+
const documentId = getDocumentId(document)
|
|
437
510
|
if (!documentId) return null
|
|
438
511
|
|
|
439
512
|
const permissions = getDocumentPermissions(this.state, documentId)
|
|
@@ -473,25 +546,7 @@ export class SharingProvider extends Component {
|
|
|
473
546
|
const drivePermissionCollection = client.collection('io.cozy.permissions', {
|
|
474
547
|
driveId: document.driveId
|
|
475
548
|
})
|
|
476
|
-
|
|
477
|
-
if (!documentId) return []
|
|
478
|
-
|
|
479
|
-
const resp = await drivePermissionCollection.findLinksByIds([documentId])
|
|
480
|
-
const permissions = resp.data || []
|
|
481
|
-
|
|
482
|
-
const existingPermissions = getDocumentPermissions(this.state, documentId)
|
|
483
|
-
const existingPermissionIds = existingPermissions.map(
|
|
484
|
-
permission => permission.id
|
|
485
|
-
)
|
|
486
|
-
const newPermissions = permissions.filter(
|
|
487
|
-
permission => !existingPermissionIds.includes(permission.id)
|
|
488
|
-
)
|
|
489
|
-
|
|
490
|
-
if (newPermissions.length > 0) {
|
|
491
|
-
this.dispatch(addSharingLink(newPermissions))
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return permissions
|
|
549
|
+
return this.fetchSharingLinks(document, drivePermissionCollection)
|
|
495
550
|
}
|
|
496
551
|
|
|
497
552
|
updateSharedDrivePermissions = async (
|
|
@@ -522,12 +577,20 @@ export class SharingProvider extends Component {
|
|
|
522
577
|
* @param {string[]} options.verbs The new verbs to use for the permission, eg. ['GET']
|
|
523
578
|
* @param {string} [options.expiresAt] The new expiration date for the permission
|
|
524
579
|
* @param {string} [options.password] The new password for the permission
|
|
580
|
+
* @param {object[]|null} [permissionsToUpdate] Permission documents to update
|
|
581
|
+
* instead of the document's permissions from state
|
|
525
582
|
*
|
|
526
583
|
* @return {Array}
|
|
527
584
|
*/
|
|
528
|
-
updateDocumentPermissions = async (
|
|
585
|
+
updateDocumentPermissions = async (
|
|
586
|
+
document,
|
|
587
|
+
options,
|
|
588
|
+
permissionsToUpdate = null
|
|
589
|
+
) => {
|
|
529
590
|
const { verbs, expiresAt, password } = options
|
|
530
|
-
const
|
|
591
|
+
const documentId = getDocumentId(document)
|
|
592
|
+
const permissions =
|
|
593
|
+
permissionsToUpdate || getDocumentPermissions(this.state, documentId)
|
|
531
594
|
|
|
532
595
|
if (!permissions || permissions.length === 0) {
|
|
533
596
|
return []
|
|
@@ -535,10 +598,11 @@ export class SharingProvider extends Component {
|
|
|
535
598
|
|
|
536
599
|
const responses = await Promise.all(
|
|
537
600
|
permissions.map(async permissionDocument => {
|
|
538
|
-
const updatedPermissions =
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
601
|
+
const updatedPermissions = Object.fromEntries(
|
|
602
|
+
Object.entries(permissionDocument.attributes.permissions).map(
|
|
603
|
+
([permissionType, rules]) => [permissionType, { ...rules, verbs }]
|
|
604
|
+
)
|
|
605
|
+
)
|
|
542
606
|
|
|
543
607
|
const resp = document.driveId
|
|
544
608
|
? await this.updateSharedDrivePermissions(
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { act, render, screen } from '@testing-library/react'
|
|
2
|
+
import { endOfDay } from 'date-fns'
|
|
2
3
|
import React from 'react'
|
|
3
4
|
|
|
4
5
|
import { createMockClient } from 'cozy-client'
|
|
@@ -23,6 +24,24 @@ const AppWrapper = ({ children, client, isPublic }) => {
|
|
|
23
24
|
)
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
const setupProvider = (client, options = {}, state = reducer()) => {
|
|
28
|
+
const provider = new SharingProvider({ client, ...options })
|
|
29
|
+
provider.state = {
|
|
30
|
+
...provider.state,
|
|
31
|
+
...state,
|
|
32
|
+
onShared: provider.state.onShared
|
|
33
|
+
}
|
|
34
|
+
provider.dispatch = jest.fn(action => {
|
|
35
|
+
const onShared = provider.state.onShared
|
|
36
|
+
provider.state = {
|
|
37
|
+
...provider.state,
|
|
38
|
+
...reducer(provider.state, action),
|
|
39
|
+
onShared
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
return provider
|
|
43
|
+
}
|
|
44
|
+
|
|
26
45
|
describe('allLoaded', () => {
|
|
27
46
|
const client = createMockClient({})
|
|
28
47
|
client.isLogged = true
|
|
@@ -142,11 +161,7 @@ describe('shareByLink', () => {
|
|
|
142
161
|
createSharingLink: mockCreateSharingLink
|
|
143
162
|
})
|
|
144
163
|
|
|
145
|
-
const provider =
|
|
146
|
-
provider.state = reducer()
|
|
147
|
-
provider.dispatch = jest.fn(action => {
|
|
148
|
-
provider.state = reducer(provider.state, action)
|
|
149
|
-
})
|
|
164
|
+
const provider = setupProvider(mockClient)
|
|
150
165
|
|
|
151
166
|
await provider.shareByLink(driveFile, { verbs: ['GET'] })
|
|
152
167
|
|
|
@@ -181,12 +196,8 @@ describe('shareByLink', () => {
|
|
|
181
196
|
createSharingLink: mockCreateSharingLink
|
|
182
197
|
})
|
|
183
198
|
|
|
184
|
-
const provider =
|
|
185
|
-
provider.state = reducer()
|
|
199
|
+
const provider = setupProvider(mockClient)
|
|
186
200
|
provider.permissionCol = { createSharingLink: mockCreateSharingLink }
|
|
187
|
-
provider.dispatch = jest.fn(action => {
|
|
188
|
-
provider.state = reducer(provider.state, action)
|
|
189
|
-
})
|
|
190
201
|
|
|
191
202
|
await provider.shareByLink(regularFile, { verbs: ['GET'] })
|
|
192
203
|
|
|
@@ -197,6 +208,163 @@ describe('shareByLink', () => {
|
|
|
197
208
|
})
|
|
198
209
|
})
|
|
199
210
|
|
|
211
|
+
describe('ensureSharingLink', () => {
|
|
212
|
+
const permission = {
|
|
213
|
+
id: 'permission-id',
|
|
214
|
+
attributes: {
|
|
215
|
+
permissions: {
|
|
216
|
+
rule0: {
|
|
217
|
+
verbs: ['GET'],
|
|
218
|
+
values: ['file-id']
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
shortcodes: { code: 'share-code' }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const document = { _id: 'file-id', name: 'invoice.pdf' }
|
|
225
|
+
const linkAccess = {
|
|
226
|
+
editingRights: 'write',
|
|
227
|
+
dateEnabled: false,
|
|
228
|
+
selectedDate: null,
|
|
229
|
+
passwordEnabled: false,
|
|
230
|
+
password: ''
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
it('fetches regular links with the regular permission API', async () => {
|
|
234
|
+
const client = createMockClient({})
|
|
235
|
+
client.getStackClient = () => ({ uri: 'https://cozy.example' })
|
|
236
|
+
client.collection = jest.fn().mockReturnValue({})
|
|
237
|
+
client.capabilities = { flat_subdomains: true }
|
|
238
|
+
const provider = setupProvider(client, {
|
|
239
|
+
doctype: 'io.cozy.files',
|
|
240
|
+
documentType: 'Files'
|
|
241
|
+
})
|
|
242
|
+
const unrelatedPermission = {
|
|
243
|
+
...permission,
|
|
244
|
+
id: 'unrelated-permission-id',
|
|
245
|
+
attributes: {
|
|
246
|
+
...permission.attributes,
|
|
247
|
+
permissions: {
|
|
248
|
+
rule0: {
|
|
249
|
+
...permission.attributes.permissions.rule0,
|
|
250
|
+
values: ['other']
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const findLinksByDoctype = jest
|
|
256
|
+
.fn()
|
|
257
|
+
.mockResolvedValue({ data: [permission, unrelatedPermission] })
|
|
258
|
+
const findLinksByIds = jest.fn(() => {
|
|
259
|
+
throw new Error('findLinksByIds is only supported for shared drives')
|
|
260
|
+
})
|
|
261
|
+
provider.permissionCol = { findLinksByDoctype, findLinksByIds }
|
|
262
|
+
provider.updateDocumentPermissions = jest
|
|
263
|
+
.fn()
|
|
264
|
+
.mockResolvedValue([{ data: permission }])
|
|
265
|
+
|
|
266
|
+
await provider.ensureSharingLink(document, linkAccess)
|
|
267
|
+
|
|
268
|
+
expect(findLinksByDoctype).toHaveBeenCalledWith('io.cozy.files')
|
|
269
|
+
expect(findLinksByIds).not.toHaveBeenCalled()
|
|
270
|
+
expect(provider.updateDocumentPermissions).toHaveBeenCalledWith(
|
|
271
|
+
{ ...document, id: 'file-id' },
|
|
272
|
+
expect.any(Object),
|
|
273
|
+
[permission]
|
|
274
|
+
)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('updates an existing link and returns its public URL', async () => {
|
|
278
|
+
const client = createMockClient({})
|
|
279
|
+
client.getStackClient = () => ({ uri: 'https://cozy.example' })
|
|
280
|
+
client.collection = jest.fn().mockReturnValue({})
|
|
281
|
+
client.capabilities = { flat_subdomains: true }
|
|
282
|
+
const provider = setupProvider(client, { documentType: 'Files' })
|
|
283
|
+
provider.fetchSharingLinks = jest.fn().mockResolvedValue([permission])
|
|
284
|
+
provider.updateDocumentPermissions = jest
|
|
285
|
+
.fn()
|
|
286
|
+
.mockResolvedValue([{ data: permission }])
|
|
287
|
+
|
|
288
|
+
const result = await provider.ensureSharingLink(document, linkAccess)
|
|
289
|
+
|
|
290
|
+
expect(provider.updateDocumentPermissions).toHaveBeenCalledWith(
|
|
291
|
+
{ ...document, id: 'file-id' },
|
|
292
|
+
{
|
|
293
|
+
verbs: ['GET', 'POST', 'PUT', 'PATCH'],
|
|
294
|
+
expiresAt: '',
|
|
295
|
+
password: ''
|
|
296
|
+
},
|
|
297
|
+
[permission]
|
|
298
|
+
)
|
|
299
|
+
expect(result.documentId).toBe('file-id')
|
|
300
|
+
expect(result.url).toContain('sharecode=share-code')
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('creates a link when no existing permission is found', async () => {
|
|
304
|
+
const client = createMockClient({})
|
|
305
|
+
client.getStackClient = () => ({ uri: 'https://cozy.example' })
|
|
306
|
+
client.collection = jest.fn().mockReturnValue({})
|
|
307
|
+
client.capabilities = { flat_subdomains: true }
|
|
308
|
+
const provider = setupProvider(client, { documentType: 'Files' })
|
|
309
|
+
provider.fetchSharingLinks = jest.fn().mockResolvedValue([])
|
|
310
|
+
provider.shareByLink = jest.fn().mockResolvedValue({ data: permission })
|
|
311
|
+
|
|
312
|
+
await provider.ensureSharingLink(document, linkAccess)
|
|
313
|
+
|
|
314
|
+
expect(provider.shareByLink).toHaveBeenCalledWith(
|
|
315
|
+
document,
|
|
316
|
+
expect.objectContaining({ verbs: ['GET', 'POST', 'PUT', 'PATCH'] })
|
|
317
|
+
)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('sends the selected expiry and password when updating a link', async () => {
|
|
321
|
+
const client = createMockClient({})
|
|
322
|
+
client.getStackClient = () => ({ uri: 'https://cozy.example' })
|
|
323
|
+
client.collection = jest.fn().mockReturnValue({})
|
|
324
|
+
client.capabilities = { flat_subdomains: true }
|
|
325
|
+
const provider = setupProvider(client, { documentType: 'Files' })
|
|
326
|
+
const selectedDate = new Date('2100-01-01T12:00:00.000Z')
|
|
327
|
+
provider.fetchSharingLinks = jest.fn().mockResolvedValue([permission])
|
|
328
|
+
provider.updateDocumentPermissions = jest
|
|
329
|
+
.fn()
|
|
330
|
+
.mockResolvedValue([{ data: permission }])
|
|
331
|
+
|
|
332
|
+
await provider.ensureSharingLink(document, {
|
|
333
|
+
editingRights: 'readOnly',
|
|
334
|
+
dateEnabled: true,
|
|
335
|
+
selectedDate,
|
|
336
|
+
passwordEnabled: true,
|
|
337
|
+
password: 'secret'
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
expect(provider.updateDocumentPermissions).toHaveBeenCalledWith(
|
|
341
|
+
{ ...document, id: 'file-id' },
|
|
342
|
+
{
|
|
343
|
+
verbs: ['GET'],
|
|
344
|
+
expiresAt: endOfDay(selectedDate).toISOString(),
|
|
345
|
+
password: 'secret'
|
|
346
|
+
},
|
|
347
|
+
[permission]
|
|
348
|
+
)
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
it('updates permissions discovered outside the current store state', async () => {
|
|
352
|
+
const client = createMockClient({})
|
|
353
|
+
client.getStackClient = () => ({ uri: 'https://cozy.example' })
|
|
354
|
+
client.collection = jest.fn().mockReturnValue({})
|
|
355
|
+
client.capabilities = { flat_subdomains: true }
|
|
356
|
+
const provider = setupProvider(client, { documentType: 'Files' })
|
|
357
|
+
const updatePermission = jest.fn().mockResolvedValue({ data: permission })
|
|
358
|
+
provider.permissionCol = { add: updatePermission }
|
|
359
|
+
provider.fetchSharingLinks = jest.fn().mockResolvedValue([permission])
|
|
360
|
+
provider.dispatch = jest.fn()
|
|
361
|
+
|
|
362
|
+
await provider.ensureSharingLink(document, linkAccess)
|
|
363
|
+
|
|
364
|
+
expect(updatePermission).toHaveBeenCalledTimes(1)
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
|
|
200
368
|
describe('revokeSharingLink', () => {
|
|
201
369
|
const PERM = {
|
|
202
370
|
type: 'io.cozy.permissions',
|
|
@@ -217,11 +385,11 @@ describe('revokeSharingLink', () => {
|
|
|
217
385
|
const mockClient = createMockClient({})
|
|
218
386
|
mockClient.getStackClient = () => ({ uri: 'http://cozy.local' })
|
|
219
387
|
mockClient.collection = jest.fn().mockReturnValue({ destroy: mockDestroy })
|
|
220
|
-
provider =
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
388
|
+
provider = setupProvider(
|
|
389
|
+
mockClient,
|
|
390
|
+
{},
|
|
391
|
+
reducer(undefined, addSharingLink(PERM))
|
|
392
|
+
)
|
|
225
393
|
})
|
|
226
394
|
|
|
227
395
|
it('uses permissionCol.destroy for a regular file', async () => {
|
|
@@ -281,9 +449,12 @@ describe('updateDocumentPermissions', () => {
|
|
|
281
449
|
const mockClient = createMockClient({})
|
|
282
450
|
mockClient.collection = jest.fn().mockReturnValue({ add: mockAdd })
|
|
283
451
|
|
|
284
|
-
const provider = new SharingProvider({ client: mockClient })
|
|
285
452
|
// Seed the state with exactly one permission (as it would be after a correct shareByLink)
|
|
286
|
-
provider
|
|
453
|
+
const provider = setupProvider(
|
|
454
|
+
mockClient,
|
|
455
|
+
{},
|
|
456
|
+
reducer(undefined, addSharingLink(PERM_DRIVE_FILE))
|
|
457
|
+
)
|
|
287
458
|
provider.dispatch = jest.fn()
|
|
288
459
|
|
|
289
460
|
await provider.updateDocumentPermissions(driveFile, {
|
|
@@ -354,17 +525,11 @@ describe('shared drive recipient revocation', () => {
|
|
|
354
525
|
mockClient.getStackClient = () => ({ uri: 'http://cozy.local' })
|
|
355
526
|
mockClient.collection = jest.fn().mockReturnValue({})
|
|
356
527
|
|
|
357
|
-
provider =
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
provider.dispatch = jest.fn(action => {
|
|
363
|
-
provider.state = {
|
|
364
|
-
...provider.state,
|
|
365
|
-
...reducer(provider.state, action)
|
|
366
|
-
}
|
|
367
|
-
})
|
|
528
|
+
provider = setupProvider(
|
|
529
|
+
mockClient,
|
|
530
|
+
{},
|
|
531
|
+
reducer(undefined, receiveSharings({ sharings: [SHARED_DRIVE] }))
|
|
532
|
+
)
|
|
368
533
|
sharingCol = {
|
|
369
534
|
addRecipients: jest.fn(),
|
|
370
535
|
create: jest.fn().mockResolvedValue({ data: NEW_SHARING }),
|
|
@@ -437,11 +602,7 @@ describe('fetchSharedDriveSharingLinks', () => {
|
|
|
437
602
|
findLinksByIds: mockFindLinksByIds
|
|
438
603
|
})
|
|
439
604
|
|
|
440
|
-
const provider =
|
|
441
|
-
provider.state = reducer()
|
|
442
|
-
provider.dispatch = jest.fn(action => {
|
|
443
|
-
provider.state = reducer(provider.state, action)
|
|
444
|
-
})
|
|
605
|
+
const provider = setupProvider(mockClient)
|
|
445
606
|
|
|
446
607
|
const result = await provider.fetchSharedDriveSharingLinks(driveFile)
|
|
447
608
|
|
|
@@ -463,7 +624,7 @@ describe('fetchSharedDriveSharingLinks', () => {
|
|
|
463
624
|
findLinksByIds
|
|
464
625
|
})
|
|
465
626
|
|
|
466
|
-
const provider =
|
|
627
|
+
const provider = setupProvider(mockClient)
|
|
467
628
|
provider.dispatch = jest.fn()
|
|
468
629
|
|
|
469
630
|
const result = await provider.fetchSharedDriveSharingLinks({
|
|
@@ -484,11 +645,7 @@ describe('fetchSharedDriveSharingLinks', () => {
|
|
|
484
645
|
findLinksByIds: mockFindLinksByIds
|
|
485
646
|
})
|
|
486
647
|
|
|
487
|
-
const provider =
|
|
488
|
-
provider.state = reducer()
|
|
489
|
-
provider.dispatch = jest.fn(action => {
|
|
490
|
-
provider.state = reducer(provider.state, action)
|
|
491
|
-
})
|
|
648
|
+
const provider = setupProvider(mockClient)
|
|
492
649
|
|
|
493
650
|
await provider.fetchSharedDriveSharingLinks({
|
|
494
651
|
id: 'file_in_drive',
|
|
@@ -505,7 +662,7 @@ describe('fetchSharedDriveSharingLinks', () => {
|
|
|
505
662
|
findLinksByIds: mockFindLinksByIds
|
|
506
663
|
})
|
|
507
664
|
|
|
508
|
-
const provider =
|
|
665
|
+
const provider = setupProvider(mockClient)
|
|
509
666
|
provider.dispatch = jest.fn()
|
|
510
667
|
|
|
511
668
|
const result = await provider.fetchSharedDriveSharingLinks(driveFile)
|
|
@@ -550,11 +707,7 @@ describe('shareByLink shared drive 409 recovery', () => {
|
|
|
550
707
|
findLinksByIds: mockFindLinksByIds
|
|
551
708
|
})
|
|
552
709
|
|
|
553
|
-
const provider =
|
|
554
|
-
provider.state = reducer()
|
|
555
|
-
provider.dispatch = jest.fn(action => {
|
|
556
|
-
provider.state = reducer(provider.state, action)
|
|
557
|
-
})
|
|
710
|
+
const provider = setupProvider(mockClient)
|
|
558
711
|
|
|
559
712
|
const resp = await provider.shareByLink(driveFile, { verbs: ['GET'] })
|
|
560
713
|
|
|
@@ -575,11 +728,11 @@ describe('shareByLink shared drive 409 recovery', () => {
|
|
|
575
728
|
findLinksByIds: mockFindLinksByIds
|
|
576
729
|
})
|
|
577
730
|
|
|
578
|
-
const provider =
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
731
|
+
const provider = setupProvider(
|
|
732
|
+
mockClient,
|
|
733
|
+
{},
|
|
734
|
+
reducer(undefined, addSharingLink(PERM_DRIVE_FILE))
|
|
735
|
+
)
|
|
583
736
|
|
|
584
737
|
const resp = await provider.shareByLink(driveFile, { verbs: ['GET'] })
|
|
585
738
|
|
|
@@ -599,8 +752,7 @@ describe('shareByLink shared drive 409 recovery', () => {
|
|
|
599
752
|
findLinksByIds: mockFindLinksByIds
|
|
600
753
|
})
|
|
601
754
|
|
|
602
|
-
const provider =
|
|
603
|
-
provider.state = reducer()
|
|
755
|
+
const provider = setupProvider(mockClient)
|
|
604
756
|
provider.dispatch = jest.fn()
|
|
605
757
|
|
|
606
758
|
await expect(
|
|
@@ -629,18 +781,18 @@ describe('updateSharingMemberType', () => {
|
|
|
629
781
|
collection: jest.fn().mockReturnValue({})
|
|
630
782
|
}
|
|
631
783
|
|
|
632
|
-
instance =
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
784
|
+
instance = setupProvider(
|
|
785
|
+
mockClient,
|
|
786
|
+
{},
|
|
787
|
+
{
|
|
788
|
+
...reducer(),
|
|
789
|
+
sharings: [mockSharing]
|
|
790
|
+
}
|
|
791
|
+
)
|
|
637
792
|
instance.sharingCol = {
|
|
638
793
|
setReadOnly: jest.fn().mockResolvedValue({}),
|
|
639
794
|
setReadWrite: jest.fn().mockResolvedValue({})
|
|
640
795
|
}
|
|
641
|
-
instance.dispatch = jest.fn(action => {
|
|
642
|
-
instance.state = { ...instance.state, ...reducer(instance.state, action) }
|
|
643
|
-
})
|
|
644
796
|
})
|
|
645
797
|
|
|
646
798
|
it('should throw when sharing is not found', async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Icon, CloudPlusOutlined } from '@linagora/twake-icons'
|
|
2
1
|
import React, { forwardRef } from 'react'
|
|
3
2
|
|
|
3
|
+
import { Icon, CloudPlusOutlined } from '@linagora/twake-icons'
|
|
4
4
|
import ActionsMenuItem from 'cozy-ui/transpiled/react/ActionsMenu/ActionsMenuItem'
|
|
5
5
|
import ListItemIcon from 'cozy-ui/transpiled/react/ListItemIcon'
|
|
6
6
|
import ListItemText from 'cozy-ui/transpiled/react/ListItemText'
|