cozy-sharing 38.1.9 → 38.1.10

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 CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [38.1.10](https://github.com/cozy/cozy-libs/compare/cozy-sharing@38.1.9...cozy-sharing@38.1.10) (2026-10-05)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **cozy-sharing:** Respect nested permissions in hasWriteAccess ([b04a8aa](https://github.com/cozy/cozy-libs/commit/b04a8aa6bda15e5ef5dfa357c3c75a5fdc040e1a))
11
+
6
12
  ## [38.1.9](https://github.com/cozy/cozy-libs/compare/cozy-sharing@38.1.8...cozy-sharing@38.1.9) (2026-10-04)
7
13
 
8
14
  **Note:** Version bump only for package cozy-sharing
@@ -1225,21 +1225,21 @@ export var SharingProvider = /*#__PURE__*/function (_Component) {
1225
1225
  }());
1226
1226
 
1227
1227
  _defineProperty(_this2, "hasWriteAccess", function (docId) {
1228
+ var _this2$state$byDocId;
1229
+
1228
1230
  var driveId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
1229
1231
 
1230
1232
  var instanceUri = _this2.props.client.getStackClient().uri;
1231
- /** Split another case for checking sharing type for shared drive
1232
- * In case of shared drive, we just check if shared drive has write access or not.
1233
- * All the files / folders inside must follow the sharing rule of shared drive.
1234
- * `driveId` only exist in files / folders from recipient, so we don't need to check it belong to owner or not.
1235
- */
1236
1233
 
1234
+ if ((_this2$state$byDocId = _this2.state.byDocId) !== null && _this2$state$byDocId !== void 0 && _this2$state$byDocId[docId]) {
1235
+ return _isOwner(_this2.state, docId) || _getSharingType(_this2.state, docId, instanceUri) === SHARING_TYPE.TWO_WAY;
1236
+ }
1237
1237
 
1238
1238
  if (driveId) {
1239
1239
  return getSharedDriveSharingType(_this2.state, driveId, instanceUri) === SHARING_TYPE.TWO_WAY;
1240
1240
  }
1241
1241
 
1242
- return !_this2.state.byDocId || !_this2.state.byDocId[docId] || _isOwner(_this2.state, docId) || _getSharingType(_this2.state, docId, instanceUri) === SHARING_TYPE.TWO_WAY;
1242
+ return true;
1243
1243
  });
1244
1244
 
1245
1245
  var _instanceUri = props.client.getStackClient().uri;
@@ -1714,83 +1714,111 @@ describe('updateSharingMemberType', function () {
1714
1714
  }
1715
1715
  }, _callee39);
1716
1716
  })));
1717
- }); // TODO Convert with react-testing-library
1718
- // describe('hasWriteAccess', () => {
1719
- // it('tells if a doc is writtable', () => {
1720
- // const client = createMockClient({})
1721
- // client.stackClient.uri = 'http://cozy.tools:8080'
1722
- // const component = mount(
1723
- // <AppWrapper client={client}>
1724
- // <SharingContext.Consumer>
1725
- // {({ hasWriteAccess }) => (
1726
- // <>
1727
- // <div data-id="no-sharing">
1728
- // {hasWriteAccess('no-sharing') ? 'yes' : 'no'}
1729
- // </div>
1730
- // <div data-id="owner-doc">
1731
- // {hasWriteAccess('owner-doc') ? 'yes' : 'no'}
1732
- // </div>
1733
- // <div data-id="synced-doc">
1734
- // {hasWriteAccess('synced-doc') ? 'yes' : 'no'}
1735
- // </div>
1736
- // <div data-id="read-only-doc">
1737
- // {hasWriteAccess('read-only-doc') ? 'yes' : 'no'}
1738
- // </div>
1739
- // </>
1740
- // )}
1741
- // </SharingContext.Consumer>
1742
- // </AppWrapper>
1743
- // )
1744
- // expect(component.find('div[data-id="no-sharing"]').text()).toBe('yes')
1745
- // expect(component.find('div[data-id="owner-doc"]').text()).toBe('yes')
1746
- // expect(component.find('div[data-id="synced-doc"]').text()).toBe('yes')
1747
- // expect(component.find('div[data-id="read-only-doc"]').text()).toBe('yes')
1748
- // const provider = component.find(SharingProvider)
1749
- // provider.instance().dispatch(
1750
- // receiveSharings({
1751
- // sharings: [
1752
- // {
1753
- // id: '123',
1754
- // type: 'io.cozy.sharings',
1755
- // attributes: {
1756
- // owner: true,
1757
- // members: [
1758
- // { read_only: false, instance: 'http://cozy.tools:8080' }
1759
- // ],
1760
- // rules: [{ values: ['owner-doc'] }]
1761
- // }
1762
- // },
1763
- // {
1764
- // id: '456',
1765
- // type: 'io.cozy.sharings',
1766
- // attributes: {
1767
- // owner: false,
1768
- // members: [
1769
- // { read_only: false, instance: 'http://cozy.tools:8080' }
1770
- // ],
1771
- // rules: [
1772
- // { values: ['synced-doc'], update: 'sync', remove: 'sync' }
1773
- // ]
1774
- // }
1775
- // },
1776
- // {
1777
- // id: '789',
1778
- // type: 'io.cozy.sharings',
1779
- // attributes: {
1780
- // owner: false,
1781
- // members: [
1782
- // { read_only: true, instance: 'http://cozy.tools:8080' }
1783
- // ],
1784
- // rules: [{ values: ['read-only-doc'] }]
1785
- // }
1786
- // }
1787
- // ]
1788
- // })
1789
- // )
1790
- // component.update()
1791
- // expect(component.find('div[data-id="no-sharing"]').text()).toBe('yes')
1792
- // expect(component.find('div[data-id="owner-doc"]').text()).toBe('yes')
1793
- // expect(component.find('div[data-id="synced-doc"]').text()).toBe('yes')
1794
- // expect(component.find('div[data-id="read-only-doc"]').text()).toBe('no')
1795
- // })
1796
- // })
1717
+ });
1718
+ describe('hasWriteAccess', function () {
1719
+ var createClient = function createClient() {
1720
+ var client = createMockClient({});
1721
+ client.getStackClient().uri = 'http://cozy.tools:8080';
1722
+ return client;
1723
+ };
1724
+
1725
+ var parentSharing = {
1726
+ id: 'parent-drive',
1727
+ type: 'io.cozy.sharings',
1728
+ attributes: {
1729
+ owner: false,
1730
+ drive: true,
1731
+ members: [{
1732
+ read_only: true,
1733
+ instance: 'http://cozy.tools:8080'
1734
+ }],
1735
+ rules: [{
1736
+ values: ['parent-folder']
1737
+ }]
1738
+ }
1739
+ };
1740
+ var nestedSharing = {
1741
+ id: 'nested-drive',
1742
+ type: 'io.cozy.sharings',
1743
+ attributes: {
1744
+ owner: false,
1745
+ drive: true,
1746
+ members: [{
1747
+ read_only: false,
1748
+ instance: 'http://cozy.tools:8080'
1749
+ }],
1750
+ rules: [{
1751
+ values: ['nested-folder']
1752
+ }]
1753
+ }
1754
+ };
1755
+ var ownerSharing = {
1756
+ id: 'owner-drive',
1757
+ type: 'io.cozy.sharings',
1758
+ attributes: {
1759
+ owner: true,
1760
+ drive: true,
1761
+ members: [{
1762
+ read_only: false,
1763
+ instance: 'http://cozy.tools:8080'
1764
+ }],
1765
+ rules: [{
1766
+ values: ['owner-folder']
1767
+ }]
1768
+ }
1769
+ };
1770
+ it('returns true when document is not shared and no driveId is provided', function () {
1771
+ var provider = setupProvider(createClient());
1772
+ expect(provider.hasWriteAccess('unshared-doc')).toBe(true);
1773
+ });
1774
+ it('returns true when user is owner of the shared document', function () {
1775
+ var provider = setupProvider(createClient());
1776
+ provider.dispatch(receiveSharings({
1777
+ sharings: [ownerSharing],
1778
+ permissions: []
1779
+ }));
1780
+ expect(provider.hasWriteAccess('owner-folder')).toBe(true);
1781
+ expect(provider.hasWriteAccess('owner-folder', 'owner-drive')).toBe(true);
1782
+ });
1783
+ it('returns false for viewer of a shared document', function () {
1784
+ var provider = setupProvider(createClient());
1785
+ provider.dispatch(receiveSharings({
1786
+ sharings: [parentSharing],
1787
+ permissions: []
1788
+ }));
1789
+ expect(provider.hasWriteAccess('parent-folder')).toBe(false);
1790
+ });
1791
+ it('returns true for editor of a shared document', function () {
1792
+ var provider = setupProvider(createClient());
1793
+ provider.dispatch(receiveSharings({
1794
+ sharings: [nestedSharing],
1795
+ permissions: []
1796
+ }));
1797
+ expect(provider.hasWriteAccess('nested-folder')).toBe(true);
1798
+ });
1799
+ it('returns drive write access when document has no explicit sharing', function () {
1800
+ var provider = setupProvider(createClient());
1801
+ provider.dispatch(receiveSharings({
1802
+ sharings: [parentSharing, nestedSharing],
1803
+ permissions: []
1804
+ }));
1805
+ expect(provider.hasWriteAccess('child-file', 'parent-drive')).toBe(false);
1806
+ expect(provider.hasWriteAccess('child-file', 'nested-drive')).toBe(true);
1807
+ });
1808
+ it('takes nested document permissions into account when driveId is provided (issue linagora/twake-drive#4220)', function () {
1809
+ var provider = setupProvider(createClient());
1810
+ provider.dispatch(receiveSharings({
1811
+ sharings: [parentSharing, nestedSharing],
1812
+ permissions: []
1813
+ }));
1814
+ expect(provider.hasWriteAccess('nested-folder', 'parent-drive')).toBe(true);
1815
+ });
1816
+ it('takes nested restricted permissions into account when driveId is editor', function () {
1817
+ var provider = setupProvider(createClient());
1818
+ provider.dispatch(receiveSharings({
1819
+ sharings: [parentSharing, nestedSharing],
1820
+ permissions: []
1821
+ }));
1822
+ expect(provider.hasWriteAccess('parent-folder', 'nested-drive')).toBe(false);
1823
+ });
1824
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "38.1.9",
3
+ "version": "38.1.10",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -58,7 +58,7 @@
58
58
  "cozy-intent": "^2.31.1",
59
59
  "cozy-minilog": "^3.10.1",
60
60
  "cozy-ui": "^140.5.0",
61
- "cozy-ui-plus": "^13.0.13",
61
+ "cozy-ui-plus": "^13.0.14",
62
62
  "jest": "30.3.0",
63
63
  "jest-environment-jsdom": "30.3.0",
64
64
  "react": "16.12.0",
@@ -84,5 +84,5 @@
84
84
  "sideEffects": [
85
85
  "*.css"
86
86
  ],
87
- "gitHead": "02232de9e42ac348ae884a19e7ebfc981dd9cc63"
87
+ "gitHead": "17bfcc354f9462050e38fa5c826b4e2cc91fedd3"
88
88
  }
@@ -718,11 +718,13 @@ export class SharingProvider extends Component {
718
718
  hasWriteAccess = (docId, driveId = undefined) => {
719
719
  const instanceUri = this.props.client.getStackClient().uri
720
720
 
721
- /** Split another case for checking sharing type for shared drive
722
- * In case of shared drive, we just check if shared drive has write access or not.
723
- * All the files / folders inside must follow the sharing rule of shared drive.
724
- * `driveId` only exist in files / folders from recipient, so we don't need to check it belong to owner or not.
725
- */
721
+ if (this.state.byDocId?.[docId]) {
722
+ return (
723
+ isOwner(this.state, docId) ||
724
+ getSharingType(this.state, docId, instanceUri) === SHARING_TYPE.TWO_WAY
725
+ )
726
+ }
727
+
726
728
  if (driveId) {
727
729
  return (
728
730
  getSharedDriveSharingType(this.state, driveId, instanceUri) ===
@@ -730,12 +732,7 @@ export class SharingProvider extends Component {
730
732
  )
731
733
  }
732
734
 
733
- return (
734
- !this.state.byDocId ||
735
- !this.state.byDocId[docId] ||
736
- isOwner(this.state, docId) ||
737
- getSharingType(this.state, docId, instanceUri) === SHARING_TYPE.TWO_WAY
738
- )
735
+ return true
739
736
  }
740
737
 
741
738
  render() {
@@ -1330,88 +1330,107 @@ describe('updateSharingMemberType', () => {
1330
1330
  })
1331
1331
  })
1332
1332
 
1333
- // TODO Convert with react-testing-library
1334
- // describe('hasWriteAccess', () => {
1335
- // it('tells if a doc is writtable', () => {
1336
- // const client = createMockClient({})
1337
- // client.stackClient.uri = 'http://cozy.tools:8080'
1338
-
1339
- // const component = mount(
1340
- // <AppWrapper client={client}>
1341
- // <SharingContext.Consumer>
1342
- // {({ hasWriteAccess }) => (
1343
- // <>
1344
- // <div data-id="no-sharing">
1345
- // {hasWriteAccess('no-sharing') ? 'yes' : 'no'}
1346
- // </div>
1347
- // <div data-id="owner-doc">
1348
- // {hasWriteAccess('owner-doc') ? 'yes' : 'no'}
1349
- // </div>
1350
- // <div data-id="synced-doc">
1351
- // {hasWriteAccess('synced-doc') ? 'yes' : 'no'}
1352
- // </div>
1353
- // <div data-id="read-only-doc">
1354
- // {hasWriteAccess('read-only-doc') ? 'yes' : 'no'}
1355
- // </div>
1356
- // </>
1357
- // )}
1358
- // </SharingContext.Consumer>
1359
- // </AppWrapper>
1360
- // )
1361
-
1362
- // expect(component.find('div[data-id="no-sharing"]').text()).toBe('yes')
1363
- // expect(component.find('div[data-id="owner-doc"]').text()).toBe('yes')
1364
- // expect(component.find('div[data-id="synced-doc"]').text()).toBe('yes')
1365
- // expect(component.find('div[data-id="read-only-doc"]').text()).toBe('yes')
1366
-
1367
- // const provider = component.find(SharingProvider)
1368
- // provider.instance().dispatch(
1369
- // receiveSharings({
1370
- // sharings: [
1371
- // {
1372
- // id: '123',
1373
- // type: 'io.cozy.sharings',
1374
- // attributes: {
1375
- // owner: true,
1376
- // members: [
1377
- // { read_only: false, instance: 'http://cozy.tools:8080' }
1378
- // ],
1379
- // rules: [{ values: ['owner-doc'] }]
1380
- // }
1381
- // },
1382
- // {
1383
- // id: '456',
1384
- // type: 'io.cozy.sharings',
1385
- // attributes: {
1386
- // owner: false,
1387
- // members: [
1388
- // { read_only: false, instance: 'http://cozy.tools:8080' }
1389
- // ],
1390
- // rules: [
1391
- // { values: ['synced-doc'], update: 'sync', remove: 'sync' }
1392
- // ]
1393
- // }
1394
- // },
1395
- // {
1396
- // id: '789',
1397
- // type: 'io.cozy.sharings',
1398
- // attributes: {
1399
- // owner: false,
1400
- // members: [
1401
- // { read_only: true, instance: 'http://cozy.tools:8080' }
1402
- // ],
1403
- // rules: [{ values: ['read-only-doc'] }]
1404
- // }
1405
- // }
1406
- // ]
1407
- // })
1408
- // )
1409
-
1410
- // component.update()
1411
-
1412
- // expect(component.find('div[data-id="no-sharing"]').text()).toBe('yes')
1413
- // expect(component.find('div[data-id="owner-doc"]').text()).toBe('yes')
1414
- // expect(component.find('div[data-id="synced-doc"]').text()).toBe('yes')
1415
- // expect(component.find('div[data-id="read-only-doc"]').text()).toBe('no')
1416
- // })
1417
- // })
1333
+ describe('hasWriteAccess', () => {
1334
+ const createClient = () => {
1335
+ const client = createMockClient({})
1336
+ client.getStackClient().uri = 'http://cozy.tools:8080'
1337
+ return client
1338
+ }
1339
+
1340
+ const parentSharing = {
1341
+ id: 'parent-drive',
1342
+ type: 'io.cozy.sharings',
1343
+ attributes: {
1344
+ owner: false,
1345
+ drive: true,
1346
+ members: [{ read_only: true, instance: 'http://cozy.tools:8080' }],
1347
+ rules: [{ values: ['parent-folder'] }]
1348
+ }
1349
+ }
1350
+
1351
+ const nestedSharing = {
1352
+ id: 'nested-drive',
1353
+ type: 'io.cozy.sharings',
1354
+ attributes: {
1355
+ owner: false,
1356
+ drive: true,
1357
+ members: [{ read_only: false, instance: 'http://cozy.tools:8080' }],
1358
+ rules: [{ values: ['nested-folder'] }]
1359
+ }
1360
+ }
1361
+
1362
+ const ownerSharing = {
1363
+ id: 'owner-drive',
1364
+ type: 'io.cozy.sharings',
1365
+ attributes: {
1366
+ owner: true,
1367
+ drive: true,
1368
+ members: [{ read_only: false, instance: 'http://cozy.tools:8080' }],
1369
+ rules: [{ values: ['owner-folder'] }]
1370
+ }
1371
+ }
1372
+
1373
+ it('returns true when document is not shared and no driveId is provided', () => {
1374
+ const provider = setupProvider(createClient())
1375
+ expect(provider.hasWriteAccess('unshared-doc')).toBe(true)
1376
+ })
1377
+
1378
+ it('returns true when user is owner of the shared document', () => {
1379
+ const provider = setupProvider(createClient())
1380
+ provider.dispatch(
1381
+ receiveSharings({ sharings: [ownerSharing], permissions: [] })
1382
+ )
1383
+ expect(provider.hasWriteAccess('owner-folder')).toBe(true)
1384
+ expect(provider.hasWriteAccess('owner-folder', 'owner-drive')).toBe(true)
1385
+ })
1386
+
1387
+ it('returns false for viewer of a shared document', () => {
1388
+ const provider = setupProvider(createClient())
1389
+ provider.dispatch(
1390
+ receiveSharings({ sharings: [parentSharing], permissions: [] })
1391
+ )
1392
+ expect(provider.hasWriteAccess('parent-folder')).toBe(false)
1393
+ })
1394
+
1395
+ it('returns true for editor of a shared document', () => {
1396
+ const provider = setupProvider(createClient())
1397
+ provider.dispatch(
1398
+ receiveSharings({ sharings: [nestedSharing], permissions: [] })
1399
+ )
1400
+ expect(provider.hasWriteAccess('nested-folder')).toBe(true)
1401
+ })
1402
+
1403
+ it('returns drive write access when document has no explicit sharing', () => {
1404
+ const provider = setupProvider(createClient())
1405
+ provider.dispatch(
1406
+ receiveSharings({
1407
+ sharings: [parentSharing, nestedSharing],
1408
+ permissions: []
1409
+ })
1410
+ )
1411
+ expect(provider.hasWriteAccess('child-file', 'parent-drive')).toBe(false)
1412
+ expect(provider.hasWriteAccess('child-file', 'nested-drive')).toBe(true)
1413
+ })
1414
+
1415
+ it('takes nested document permissions into account when driveId is provided (issue linagora/twake-drive#4220)', () => {
1416
+ const provider = setupProvider(createClient())
1417
+ provider.dispatch(
1418
+ receiveSharings({
1419
+ sharings: [parentSharing, nestedSharing],
1420
+ permissions: []
1421
+ })
1422
+ )
1423
+ expect(provider.hasWriteAccess('nested-folder', 'parent-drive')).toBe(true)
1424
+ })
1425
+
1426
+ it('takes nested restricted permissions into account when driveId is editor', () => {
1427
+ const provider = setupProvider(createClient())
1428
+ provider.dispatch(
1429
+ receiveSharings({
1430
+ sharings: [parentSharing, nestedSharing],
1431
+ permissions: []
1432
+ })
1433
+ )
1434
+ expect(provider.hasWriteAccess('parent-folder', 'nested-drive')).toBe(false)
1435
+ })
1436
+ })