From fc456bec39446880ca6153ef903b569f041dd087 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 19 Sep 2017 12:53:35 +0200 Subject: [PATCH 1/4] check for encryption state on propfind Signed-off-by: Bjoern Schiessle --- core/js/files/client.js | 12 ++++++++++++ core/js/tests/specs/files/clientSpec.js | 13 +++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/core/js/files/client.js b/core/js/files/client.js index fa3d795d41..dc842a9d3b 100644 --- a/core/js/files/client.js +++ b/core/js/files/client.js @@ -74,6 +74,7 @@ Client.PROPERTY_PERMISSIONS = '{' + Client.NS_OWNCLOUD + '}permissions'; Client.PROPERTY_SIZE = '{' + Client.NS_OWNCLOUD + '}size'; Client.PROPERTY_GETCONTENTLENGTH = '{' + Client.NS_DAV + '}getcontentlength'; + Client.PROPERTY_ISENCRYPTED = '{' + Client.NS_DAV + '}is-encrypted'; Client.PROTOCOL_HTTP = 'http'; Client.PROTOCOL_HTTPS = 'https'; @@ -120,6 +121,10 @@ * Mount type */ [Client.NS_NEXTCLOUD, 'mount-type'], + /** + * Encryption state + */ + [Client.NS_NEXTCLOUD, 'is-encrypted'], ]; /** @@ -305,6 +310,13 @@ data.hasPreview = true; } + var isEncryptedProp = props['{' + Client.NS_NEXTCLOUD + '}is-encrypted']; + if (!_.isUndefined(isEncryptedProp)) { + data.isEncrypted = isEncryptedProp === '1'; + } else { + data.isEncrypted = false; + } + var contentType = props[Client.PROPERTY_GETCONTENTTYPE]; if (!_.isUndefined(contentType)) { data.mimetype = contentType; diff --git a/core/js/tests/specs/files/clientSpec.js b/core/js/tests/specs/files/clientSpec.js index 6593372144..ec0a0fbda4 100644 --- a/core/js/tests/specs/files/clientSpec.js +++ b/core/js/tests/specs/files/clientSpec.js @@ -224,6 +224,7 @@ describe('OC.Files.Client tests', function() { expect(props).toContain('{http://owncloud.org/ns}fileid'); expect(props).toContain('{http://owncloud.org/ns}size'); expect(props).toContain('{http://owncloud.org/ns}permissions'); + expect(props).toContain('{http://nextcloud.org/ns}is-encrypted'); }); it('sends PROPFIND to base url when empty path given', function() { client.getFolderContents(''); @@ -262,6 +263,7 @@ describe('OC.Files.Client tests', function() { expect(info.mtime).toEqual(1436535485000); expect(info.mimetype).toEqual('text/plain'); expect(info.etag).toEqual('559fcabd79a38'); + expect(info.isEncrypted).toEqual(false); // sub entry info = response[1]; @@ -274,6 +276,7 @@ describe('OC.Files.Client tests', function() { expect(info.mtime).toEqual(1436536800000); expect(info.mimetype).toEqual('httpd/unix-directory'); expect(info.etag).toEqual('66cfcabd79abb'); + expect(info.isEncrypted).toEqual(false); }); }); it('returns parent node in result if specified', function() { @@ -303,6 +306,7 @@ describe('OC.Files.Client tests', function() { expect(info.mtime).toEqual(1436522405000); expect(info.mimetype).toEqual('httpd/unix-directory'); expect(info.etag).toEqual('56cfcabd79abb'); + expect(info.isEncrypted).toEqual(false); // the two other entries follow expect(response[1].id).toEqual(51); @@ -422,6 +426,7 @@ describe('OC.Files.Client tests', function() { expect(props).toContain('{http://owncloud.org/ns}fileid'); expect(props).toContain('{http://owncloud.org/ns}size'); expect(props).toContain('{http://owncloud.org/ns}permissions'); + expect(props).toContain('{http://nextcloud.org/ns}is-encrypted'); }); it('parses the result list into a FileInfo array', function() { var promise = client.getFilteredFiles({ @@ -473,7 +478,7 @@ describe('OC.Files.Client tests', function() { describe('file info', function() { var responseXml = dav.Client.prototype.parseMultiStatus( '' + - '' + + '' + makeResponseBlock( '/owncloud/remote.php/webdav/path/to%20space/%E6%96%87%E4%BB%B6%E5%A4%B9/', { @@ -483,7 +488,8 @@ describe('OC.Files.Client tests', function() { 'oc:id': '00000011oc2d13a6a068', 'oc:fileid': '11', 'oc:permissions': 'GRDNVCK', - 'oc:size': '120' + 'oc:size': '120', + 'nc:is-encrypted': '1' }, [ 'd:getcontenttype', @@ -510,6 +516,7 @@ describe('OC.Files.Client tests', function() { expect(props).toContain('{http://owncloud.org/ns}fileid'); expect(props).toContain('{http://owncloud.org/ns}size'); expect(props).toContain('{http://owncloud.org/ns}permissions'); + expect(props).toContain('{http://nextcloud.org/ns}is-encrypted'); }); it('parses the result into a FileInfo', function() { var promise = client.getFileInfo('path/to space/文件夹'); @@ -535,6 +542,7 @@ describe('OC.Files.Client tests', function() { expect(info.mtime).toEqual(1436522405000); expect(info.mimetype).toEqual('httpd/unix-directory'); expect(info.etag).toEqual('56cfcabd79abb'); + expect(info.isEncrypted).toEqual(true); }); }); it('properly parses entry inside root', function() { @@ -583,6 +591,7 @@ describe('OC.Files.Client tests', function() { expect(info.mtime).toEqual(1436522405000); expect(info.mimetype).toEqual('httpd/unix-directory'); expect(info.etag).toEqual('56cfcabd79abb'); + expect(info.isEncrypted).toEqual(false); }); }); it('rejects promise when an error occurred', function() { From 670ac48eb73d8ab1ce44fae81a23296f10d997c0 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 19 Sep 2017 13:58:26 +0200 Subject: [PATCH 2/4] adjust permissions in web view for encrypted folders Signed-off-by: Bjoern Schiessle --- apps/files/js/filelist.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 6996e42377..8eecfd13c2 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1447,7 +1447,9 @@ path = fileData.path || this.getCurrentDirectory(), permissions = parseInt(fileData.permissions, 10) || 0; - if (fileData.isShareMountPoint) { + var isEndToEndEncrypted = (type === 'dir' && fileData.isEncrypted); + + if (!isEndToEndEncrypted && fileData.isShareMountPoint) { permissions = permissions | OC.PERMISSION_UPDATE; } From 37d8d3d858d66ccfc9b9a32606a9448bc15f5960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Mon, 20 Nov 2017 20:51:32 +0100 Subject: [PATCH 3/4] Add data attribute to file list rows telling if the file is encrypted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- apps/files/js/filelist.js | 6 +++-- apps/files/tests/js/filelistSpec.js | 38 +++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 8eecfd13c2..217a2f1489 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -953,7 +953,8 @@ type: $el.attr('data-type'), etag: $el.attr('data-etag'), permissions: parseInt($el.attr('data-permissions'), 10), - hasPreview: $el.attr('data-has-preview') === 'true' + hasPreview: $el.attr('data-has-preview') === 'true', + isEncrypted: $el.attr('data-e2eencrypted') === 'true' }; var size = $el.attr('data-size'); if (size) { @@ -1176,7 +1177,8 @@ "data-mtime": mtime, "data-etag": fileData.etag, "data-permissions": permissions, - "data-has-preview": fileData.hasPreview !== false + "data-has-preview": fileData.hasPreview !== false, + "data-e2eencrypted": fileData.isEncrypted === true }); if (dataIcon) { diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 64fc687649..e3b5eba3ec 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -217,6 +217,7 @@ describe('OCA.Files.FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); expect($tr.attr('data-mime')).toEqual('text/plain'); expect($tr.attr('data-mtime')).toEqual('123456'); + expect($tr.attr('data-e2eencrypted')).toEqual('false'); expect($tr.find('a.name').attr('href')) .toEqual(OC.webroot + '/remote.php/webdav/subdir/testName.txt'); expect($tr.find('.nametext').text().trim()).toEqual('testName.txt'); @@ -246,6 +247,7 @@ describe('OCA.Files.FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); expect($tr.attr('data-mtime')).toEqual('123456'); + expect($tr.attr('data-e2eencrypted')).toEqual('false'); expect($tr.find('.filesize').text()).toEqual('1 KB'); expect($tr.find('.date').text()).not.toEqual('?'); @@ -271,6 +273,7 @@ describe('OCA.Files.FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); expect($tr.attr('data-mime')).toBeUndefined(); expect($tr.attr('data-mtime')).toEqual('123456'); + expect($tr.attr('data-e2eencrypted')).toEqual('false'); expect($tr.find('.filesize').text()).toEqual('Pending'); expect($tr.find('.date').text()).not.toEqual('?'); @@ -293,10 +296,20 @@ describe('OCA.Files.FileList tests', function() { expect($tr.attr('data-permissions')).toEqual('31'); expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); expect($tr.attr('data-mtime')).toEqual('123456'); + expect($tr.attr('data-e2eencrypted')).toEqual('false'); expect($tr.find('.filesize').text()).toEqual('Pending'); expect($tr.find('.date').text()).not.toEqual('?'); }); + it('generates dir element with true e2eencrypted attribute when calling add() with minimal data including isEncrypted', function() { + var fileData = { + type: 'dir', + name: 'testFolder', + isEncrypted: true + }; + var $tr = fileList.add(fileData); + expect($tr.attr('data-e2eencrypted')).toEqual('true'); + }); it('generates file element with no permissions when permissions are explicitly none', function() { var fileData = { type: 'dir', @@ -2086,7 +2099,8 @@ describe('OCA.Files.FileList tests', function() { size: 12, etag: 'abc', permissions: OC.PERMISSION_ALL, - hasPreview: true + hasPreview: true, + isEncrypted: false }); expect(files[1]).toEqual({ id: 3, @@ -2097,7 +2111,8 @@ describe('OCA.Files.FileList tests', function() { size: 58009, etag: '123', permissions: OC.PERMISSION_ALL, - hasPreview: true + hasPreview: true, + isEncrypted: false }); expect(files[2]).toEqual({ id: 4, @@ -2108,7 +2123,8 @@ describe('OCA.Files.FileList tests', function() { size: 250, etag: '456', permissions: OC.PERMISSION_ALL, - hasPreview: true + hasPreview: true, + isEncrypted: false }); expect(files[0].id).toEqual(1); expect(files[0].name).toEqual('One.txt'); @@ -2130,7 +2146,8 @@ describe('OCA.Files.FileList tests', function() { size: 12, etag: 'abc', permissions: OC.PERMISSION_ALL, - hasPreview: true + hasPreview: true, + isEncrypted: false }); expect(files[1]).toEqual({ id: 4, @@ -2141,7 +2158,8 @@ describe('OCA.Files.FileList tests', function() { size: 250, etag: '456', permissions: OC.PERMISSION_ALL, - hasPreview: true + hasPreview: true, + isEncrypted: false }); }); describe('Download', function() { @@ -3231,6 +3249,16 @@ describe('OCA.Files.FileList tests', function() { expect(fileInfo.mimetype).toEqual('text/plain'); expect(fileInfo.type).toEqual('file'); expect(fileInfo.path).not.toBeDefined(); + expect(fileInfo.isEncrypted).toEqual(false); + }); + it('sets isEncrypted attribute if data includes true e2eencrypted', function() { + testFiles[3].isEncrypted = true; + + fileList.setFiles(testFiles); + $tr = fileList.findFileEl('somedir'); + + var fileInfo = fileList.elementToFile($tr); + expect(fileInfo.isEncrypted).toEqual(true); }); it('adds path attribute if available', function() { $tr.attr('data-path', '/subdir'); From 7bc28f14de8c3d77ec611a4ffd8ad48dc6093cea Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 19 Sep 2017 17:24:53 +0200 Subject: [PATCH 4/4] show e2e folder icon on encrypted folders Signed-off-by: Bjoern Schiessle --- apps/files/js/filelist.js | 5 ++++- apps/files/tests/js/filelistSpec.js | 26 +++++++++++++++++++++++++ core/img/filetypes/folder-encrypted.svg | 1 + core/js/mimetype.js | 2 ++ core/js/mimetypelist.js | 1 + core/js/share.js | 6 +++++- core/js/tests/specs/shareSpec.js | 7 +++++++ 7 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 core/img/filetypes/folder-encrypted.svg diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 217a2f1489..0add41d641 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1156,7 +1156,10 @@ if (type === 'dir') { mime = mime || 'httpd/unix-directory'; - if (fileData.mountType && fileData.mountType.indexOf('external') === 0) { + if (fileData.isEncrypted) { + icon = OC.MimeType.getIconUrl('dir-encrypted'); + dataIcon = icon; + } else if (fileData.mountType && fileData.mountType.indexOf('external') === 0) { icon = OC.MimeType.getIconUrl('dir-external'); dataIcon = icon; } diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index e3b5eba3ec..83926b24fe 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -1455,6 +1455,32 @@ describe('OCA.Files.FileList tests', function() { expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder.svg'); expect(previewLoadStub.notCalled).toEqual(true); }); + it('render encrypted folder icon for encrypted root', function() { + var fileData = { + type: 'dir', + mimetype: 'httpd/unix-directory', + name: 'test dir', + isEncrypted: true + }; + var $tr = fileList.add(fileData); + var $td = $tr.find('td.filename'); + expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-encrypted.svg'); + expect(previewLoadStub.notCalled).toEqual(true); + }); + it('render encrypted folder icon for encrypted subdir', function() { + var fileData = { + type: 'dir', + mimetype: 'httpd/unix-directory', + name: 'test dir', + isEncrypted: true + }; + var $tr = fileList.add(fileData); + var $td = $tr.find('td.filename'); + expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-encrypted.svg'); + expect(previewLoadStub.notCalled).toEqual(true); + // default icon override + expect($tr.attr('data-icon')).toEqual(OC.webroot + '/core/img/filetypes/folder-encrypted.svg'); + }); it('render external storage icon for external storage root', function() { var fileData = { type: 'dir', diff --git a/core/img/filetypes/folder-encrypted.svg b/core/img/filetypes/folder-encrypted.svg new file mode 100644 index 0000000000..e2b62a99b0 --- /dev/null +++ b/core/img/filetypes/folder-encrypted.svg @@ -0,0 +1 @@ + diff --git a/core/js/mimetype.js b/core/js/mimetype.js index ed4fedc7f8..e5a07abc95 100644 --- a/core/js/mimetype.js +++ b/core/js/mimetype.js @@ -44,6 +44,8 @@ OC.MimeType = { // Generate path if (mimeType === 'dir' && $.inArray('folder', files) !== -1) { return 'folder'; + } else if (mimeType === 'dir-encrypted' && $.inArray('folder-encrypted', files) !== -1) { + return 'folder-encrypted'; } else if (mimeType === 'dir-shared' && $.inArray('folder-shared', files) !== -1) { return 'folder-shared'; } else if (mimeType === 'dir-public' && $.inArray('folder-public', files) !== -1) { diff --git a/core/js/mimetypelist.js b/core/js/mimetypelist.js index ea513131d8..13db16c5a2 100644 --- a/core/js/mimetypelist.js +++ b/core/js/mimetypelist.js @@ -104,6 +104,7 @@ OC.MimeTypeList={ "file", "folder", "folder-drag-accept", + "folder-encrypted", "folder-external", "folder-public", "folder-shared", diff --git a/core/js/share.js b/core/js/share.js index 25d59b46fb..be90c62695 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -276,10 +276,14 @@ OC.Share = _.extend(OC.Share || {}, { $tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')'); $tr.attr('data-icon', shareFolderIcon); } else if (type === 'dir') { + var isEncrypted = $tr.attr('data-e2eencrypted'); var mountType = $tr.attr('data-mounttype'); // FIXME: duplicate of FileList._createRow logic for external folder, // need to refactor the icon logic into a single code path eventually - if (mountType && mountType.indexOf('external') === 0) { + if (isEncrypted === 'true') { + shareFolderIcon = OC.MimeType.getIconUrl('dir-encrypted'); + $tr.attr('data-icon', shareFolderIcon); + } else if (mountType && mountType.indexOf('external') === 0) { shareFolderIcon = OC.MimeType.getIconUrl('dir-external'); $tr.attr('data-icon', shareFolderIcon); } else { diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index 70c698c99a..127582ace6 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -149,6 +149,13 @@ describe('OC.Share tests', function() { checkIcon('filetypes/folder-external'); }); + it('shows encrypted icon if encrypted folder', function() { + $file.attr('data-type', 'dir'); + $file.attr('data-e2eencrypted', true); + OC.Share.markFileAsShared($file, false, false); + + checkIcon('filetypes/folder-encrypted'); + }); }); describe('displaying the recipients', function() {