Compare commits

...

224 Commits

Author SHA1 Message Date
C Montero-Luque 7b510f2510 8.1.2 2015-09-02 10:54:56 +02:00
C Montero-Luque 01fc0b53f6 8.1.2 RC1 2015-08-30 10:36:12 +02:00
Morris Jobke 04bed337ee Merge pull request #18612 from owncloud/stable8.1-newer-certs-2
[stable8.1] Use certificates that expire in 10 years
2015-08-28 18:44:10 +02:00
Lukas Reschke 0e4bdfbef3 Use certificates that expire in 10 years
🙊 🙊 🙊
2015-08-27 22:25:40 +02:00
Morris Jobke 3233b9c4ee Merge pull request #18583 from owncloud/backport-of-18582
[stable8.1] Remove last occurence of `forcessl`
2015-08-26 22:08:49 +02:00
Lukas Reschke 1850476916 Remove last occurence of forcessl
This shoudl have been adjusted as well, now it's consistent with `setMagicInCookie`. While it does not have a security impact directly some automated scanners reported this all the time.
2015-08-26 15:09:00 +02:00
Morris Jobke bcc79b0e40 Merge pull request #18579 from owncloud/backport-18279-stable8.1
[backport] also don't count group members when more than 1 ldap configs are active
2015-08-26 14:29:29 +02:00
Arthur Schiwon 1d8b5ac909 also don't count group members when more than 1 ldap configs are active 2015-08-26 11:58:36 +02:00
Joas Schilling 540ad35f3b Merge pull request #18544 from owncloud/stable8.1-backport-18439
[stable8.1] [app management] fix dependency check on install
2015-08-26 11:00:39 +02:00
Morris Jobke d2c81fd3c5 Merge pull request #18555 from owncloud/fix-gs-share-stable8.1
[stable8.1] Simply hide sharing buttons for IE8 - fixes #18011
2015-08-25 21:59:32 +02:00
Lukas Reschke c3468c7b29 Merge pull request #18559 from owncloud/ext-objectstore-stable8.1
[stable8.1] Prevent objectstore being set from client side
2015-08-25 20:56:38 +02:00
Robin McCorkell a1706f61aa Prevent objectstore being set from client side 2015-08-25 16:21:09 +01:00
Thomas Müller 7d436579cb don't load gs-share 2015-08-25 16:50:17 +02:00
Thomas Müller b2adf17433 Simply hide sharing buttons for IE8 - fixes #18011 2015-08-25 16:09:28 +02:00
Morris Jobke 76306dd5a1 [app management] fix dependency check on install 2015-08-25 14:19:54 +02:00
Morris Jobke 727cb3b2a3 Merge pull request #18446 from owncloud/ext-setuservars-string-stable8.1
[stable8.1] setUserVars() should only attempt substitution with strings
2015-08-25 09:00:22 +02:00
Morris Jobke 227e338542 Merge pull request #18487 from owncloud/add-cors-header-to-allow-client-based-check
[stable8.1] Add CORS header to status.php to allow client-based check…
2015-08-24 09:30:02 +02:00
Lukas Reschke 6dffa0fc95 [stable8.1] Add CORS header to status.php to allow client-based check in the future 2015-08-22 14:48:28 +02:00
Robin McCorkell bf1096602b Retain compatibility with updated storage configurations
String booleans are still accepted
2015-08-20 12:28:59 +01:00
Robin McCorkell d6bc77599e setUserVars() should only attempt substitution with strings 2015-08-20 02:21:45 +01:00
Morris Jobke 21c9dd284b Merge pull request #18431 from owncloud/add-warning-ca-bundle-stable8.1
[stable8.1] Add warning for not existing CA bundle when updating
2015-08-19 19:53:44 +02:00
Morris Jobke 174c4e3113 Merge pull request #18353 from owncloud/backport-block-autocomplete
[stable8.1] Add option to disable autocomplete in share dialog
2015-08-19 16:16:11 +02:00
Lukas Reschke 373d1b67b2 Add warning for not existing CA bundle when updating
For newer releases we shall use an integrity check. But that's a good alternative for now.
2015-08-19 15:13:24 +02:00
Thomas Müller 31eb4f06f7 Merge pull request #18397 from owncloud/smb-1.0.4-81
[8.1] Update Icewind/SMB to 1.0.4
2015-08-19 09:16:06 +02:00
Robin Appelman b9e5809869 update icewind/smb to 1.0.4 2015-08-18 17:36:55 +02:00
Robin Appelman 8c145541f6 update icewind/smb to 1.0.3 2015-08-18 17:36:51 +02:00
Robin McCorkell 880ba38e2c Merge pull request #18367 from owncloud/config-php-tyops-8-1
typo fixes
2015-08-17 23:42:06 +01:00
Carla Schroder 47ec44da3b typo fixes 2015-08-17 10:49:28 -07:00
Morris Jobke 60cd118371 Add option to disable autocomplete in share dialog 2015-08-17 15:45:06 +02:00
Morris Jobke 2c81ac1cf7 Merge pull request #18182 from owncloud/backport-18159-stable8.1
Backport 18159 stable8.1
2015-08-16 14:39:25 +02:00
Thomas Müller 3ea3fc7b62 Merge pull request #18274 from owncloud/issue-18261-sharing-capabilities-use-wrong-default-8.1
[8.1] Issue 18261 sharing capabilities use wrong default 8.1
2015-08-14 12:24:51 +02:00
Morris Jobke dc012be598 Merge pull request #18277 from owncloud/stable8.1-17852
[stable8.1] add test for factories
2015-08-14 00:36:56 +02:00
Jan-Christoph Borchardt 94f2b2ba8c Merge pull request #18284 from owncloud/stable8.1-backport-17785
[stable8.1] fix mobile scrolling, lower sidebar sensitivity, fix #11193
2015-08-13 23:38:46 +02:00
Jan-Christoph Borchardt 116ec56e8c Merge pull request #18288 from owncloud/stable8.1-backport-18231
[stable8.1] Show strage full warning for shared storages temporary
2015-08-13 23:38:20 +02:00
Morris Jobke 444d5d27de Show strage full warning for shared storages temporary
* removed the setDefault call because then it will always be
  reshown
  * was added with ba475d4862
* fixes #18208
2015-08-13 17:12:41 +02:00
Jan-Christoph Borchardt dcda1e1e61 fix mobile scrolling, lower sidebar sensitivity, fix #11193 2015-08-13 15:43:49 +02:00
Morris Jobke 6bcf2b9e77 Merge pull request #18257 from owncloud/stable8.1-backport-17680
[stable8.1] handle rmdir on files for ftp storages
2015-08-13 15:31:51 +02:00
Thomas Müller dbc03b68ae Merge pull request #18237 from owncloud/backport-scanfilesignature
[stable8.1] Backport scanfilesignature
2015-08-13 14:54:25 +02:00
Jan-Christoph Borchardt 94eb5f645a Merge pull request #18266 from owncloud/stable8.1-filelist-emptycontentduringmask
[stable8.1] Make sure to hide empty content message when mask is shown
2015-08-13 14:48:37 +02:00
Bernhard Posselt c31e2464c8 add test for factories
use ref for factory test

Ensure we construct SimpleContainer

Use single instance of DIContainer in routing tests
2015-08-13 13:27:32 +01:00
Joas Schilling 9e42d3ed43 Fix config map provider for tests 2015-08-13 13:09:08 +02:00
Joas Schilling d3266f5459 Fix default values of sharing capabilities
The problem is the UI used a different default than the capabilities.
So when you never touched the config, the setting in admins said "disabled"
while the capabilities said "enabled".
2015-08-13 13:01:32 +02:00
Vincent Petry dcd9154f04 Make sure to hide empty content message when mask is shown 2015-08-13 11:55:15 +02:00
Joas Schilling c8e1359c9d Add a basic unit test which notifies us about incompatible extending 2015-08-13 11:41:13 +02:00
Robin Appelman 6c8689072c handle rmdir on files for ftp storages 2015-08-13 10:54:41 +02:00
Joas Schilling e272ec0c3e Fix duplicated line in doc block 2015-08-12 15:51:29 +02:00
Vincent Petry 327f96f29b Fix scanFile signature to avoid boring warning 2015-08-12 15:40:45 +02:00
Vincent Petry 1add97feae Merge pull request #17753 from oparoz/fix-previews-for-public-uploads
[stable8.1] Fix preview of old file on public upload conflicts
2015-08-12 10:58:40 +02:00
Thomas Müller e9a1a8cf75 Merge pull request #18172 from owncloud/fix-language-of-files-activities-in-emails-8.1
[8.1] Correctly make use of the languageCode argument in the files activity extension
2015-08-12 10:23:49 +02:00
Thomas Müller 69970f83f9 Merge pull request #18139 from owncloud/stable8.1-share-permwrongvar
[stable8.1] Fix removal of share permissions when share disabled for user
2015-08-12 10:23:35 +02:00
Thomas Müller 82bffa0df2 Merge pull request #18108 from owncloud/dav-upload-updater-81
[8.1] go trough the updater when uploading over webdav
2015-08-12 10:21:09 +02:00
Thomas Müller 7c58985ca1 Merge pull request #18104 from owncloud/share-group-conflict-81
[8.1] Re-use the share entry we have for the shared storage instead of searching for it every time
2015-08-12 10:20:44 +02:00
Thomas Müller 18ec257ab2 Merge pull request #18064 from owncloud/import_root_certificates_8.1
[stable8.1] disable "ssl root certificate" settings if user can't mount external storages
2015-08-12 10:20:27 +02:00
Thomas Müller 04b409fb09 Merge pull request #17991 from owncloud/backport-correct-regex
[stable8.1] Correct regex
2015-08-12 10:20:01 +02:00
Thomas Müller 571b7f0c2c Merge pull request #17972 from owncloud/stable81-17969
[stable8.1] [admin settings] only retrieve log file size if file exists
2015-08-12 10:19:52 +02:00
Thomas Müller e94d8bbf24 Merge pull request #17957 from owncloud/stable8.1-backport-17489
[stable8.1] Fix parsing of sharetime as string
2015-08-12 10:19:31 +02:00
Thomas Müller ac04692eac Merge pull request #17951 from owncloud/backport-17464-stable8.1
[stable8.1] Backport of #17464, fix uncaught exception on not permitted file type…
2015-08-12 10:19:16 +02:00
Thomas Müller f7cb36a706 Merge pull request #17947 from owncloud/fix_move_files_8.1
[stable8.1] make sure that hooks are emitted properly on file move operation
2015-08-12 10:19:01 +02:00
michag86 c9132190ab Update installer.php 2015-08-10 18:38:22 +02:00
michag86 677bb62edf Check if archive contains a directory named like appid 2015-08-10 18:38:11 +02:00
Joas Schilling 087acc550c Correctly make use of the languageCode argument in the files activity extension 2015-08-10 14:59:51 +02:00
Frank Karlitschek ca66a705ec 8.1.1 2015-08-07 20:17:23 -04:00
Vincent Petry 89ccdcf134 Fix removal of share permissions when share disabled for user 2015-08-07 19:05:55 +02:00
Robin Appelman e0a330315d go trough the updater when uploading over webdav 2015-08-06 16:58:09 +02:00
Robin Appelman b91f002baf just pass the share instead of searching for it 2015-08-06 14:30:36 +02:00
Robin Appelman cb791c7fe2 add unit tests for share target conflict with group shares 2015-08-06 14:30:25 +02:00
Thomas Müller 1a1e19f929 Merge pull request #18054 from owncloud/shared-propagate-check-recipient-stable81
[8.1] fix infinite loops with propagating etags on reshares
2015-08-05 12:01:36 +02:00
Bjoern Schiessle 6534202573 also block certificate management in the back-end if external storages are disabled for the user 2015-08-05 10:40:49 +02:00
Bjoern Schiessle a430e75425 only add the possibility to import ssl root certificates to the personal
setting if the user can mount external storages
2015-08-05 10:40:40 +02:00
Robin Appelman 78a46dcac1 fix infinite loops with propagating etags on reshares 2015-08-04 14:05:41 +02:00
C Montero-Luque 8c9b8f4fe9 8.1.1 RC1 2015-08-03 07:16:38 -04:00
Thomas Müller a0ab86bb91 Merge pull request #18013 from owncloud/enc_always_update_file_cache_8.1
[stable8.1] let the encryption storage wrapper always update the file cache
2015-08-03 12:54:01 +02:00
Björn Schießle ab16bea5c7 Merge pull request #17990 from owncloud/enc_only_update_file_cache_once_8.1
[stable8.1] only update database on the first run
2015-08-03 11:34:24 +02:00
Bjoern Schiessle 2e8f47dfb6 always update file cache, the cache can handle partial data correctly if the file doesn't already exists in the file cache 2015-07-31 17:34:01 +02:00
Bjoern Schiessle a79a15e6c5 only update database on the first run (first run = we have a version number from the old encryption app) 2015-07-31 10:50:56 +02:00
Roeland Douma cf4f43363c Merge pull request #18003 from owncloud/backport-17805-stable8.1
[stable8.1][avatar] add error handlers for avatar setup
2015-07-31 10:46:18 +02:00
Morris Jobke ef2eeae852 [avatar] add error handlers for avatar setup
add colon to translated string

use placeholder in t()

Adding a size limitation for avatar upload

Unit test for file size

Fix typo & display server side error message
2015-07-31 07:35:01 +02:00
Lukas Reschke f7360795f8 Add more unit tests 2015-07-30 16:10:28 +02:00
Lukas Reschke c61b23561b Correct regular expressions
Previously the regex was only matching on single characters. Meaning that file names such as "👍.txt" where possible while "👍" alone never was. This check apparently never worked as expected.
2015-07-30 16:10:21 +02:00
Morris Jobke 4409aed581 [admin settings] only retrieve log file size if file exists
* fixes #17467
2015-07-29 22:00:21 +01:00
Morris Jobke a9524e250a Merge pull request #17949 from owncloud/stable8.1-settings-users-apprenderedforsearchbox
[stable8.1] Trigger "apprendered" event in users page
2015-07-29 15:03:58 +02:00
Morris Jobke 42ca905d62 tests for _parseTime with hex and empty strings 2015-07-29 14:55:30 +02:00
Morris Jobke 4d6dcec751 Fix parsing of sharetime as string
In some cases the ajax/share.php will return the share time as string.
If this is the case it would get parsed completely wrong and cause the
share dropdown to not work anymore. This change will properly cast the
string to an interger and also fallback if this is not possible.
2015-07-29 14:55:21 +02:00
Arthur Schiwon 9bfbed5e0d Backport of #17464, fix uncaught exception on not permitted file types when setting avatar
refactor integration tests to make it easier to add new ones

add integration tests for avatar update

fix uncaught exception on not permitted file types when setting avatar, fixes #17232

fix test after rebasing
2015-07-29 12:45:02 +02:00
Vincent Petry c411dcd30a Trigger "apprendered" event in users page
This will properly update the controls bar width and display the search
box initially.
2015-07-29 11:35:23 +02:00
Bjoern Schiessle 2c83115a3d make sure that we emit the hooks if a file gets moved from a subfolder to the root folder with the nodes API 2015-07-29 11:07:47 +02:00
Thomas Müller 73da1ca71d Merge pull request #17935 from owncloud/detect-old-openssl-versions-stable81
[stable8.1] Detect old NSS and OpenSSL versions
2015-07-29 09:22:23 +02:00
Thomas Müller 7f5f7711ad Merge pull request #17929 from owncloud/stable8.1-smb-storageiddoubleslash
[stable8.1] Double slash for SMB storage id for compatibility
2015-07-28 23:50:31 +02:00
Vincent Petry 891a4d2ccd Merge pull request #17928 from owncloud/fix_17925_8.1
[stable8.1] set logger in constructor
2015-07-28 18:30:14 +02:00
Lukas Reschke e0b5bea50f Add unit tests 2015-07-28 16:35:55 +02:00
Lukas Reschke 3133949418 Detect old NSS and OpenSSL versions
This will detect old NSS and OpenSSL versions and show appropriate errors in the admin interface.

Fixes https://github.com/owncloud/core/issues/17901
2015-07-28 16:02:06 +02:00
Vincent Petry dcd5666601 Double slash for SMB storage id for compatibility 2015-07-28 14:30:56 +02:00
Bjoern Schiessle fc467da7d3 set logger in constructor 2015-07-28 14:21:38 +02:00
Vincent Petry a949f6c576 Merge pull request #17911 from owncloud/fix-enc-wrapper-without-encryption-stable8.1
[stable8.1] Fix enc wrapper without encryption stable8.1
2015-07-27 19:29:52 +02:00
Thomas Müller e8cbfd16a2 Merge pull request #17903 from owncloud/fix_17898_8.1
[backport 8.1] get header size before we open the file to avoid locking exception
2015-07-27 17:12:12 +02:00
Bjoern Schiessle e25a116aa6 adujust version to the stable8.1 branch 2015-07-27 16:48:58 +02:00
Bjoern Schiessle 036ba93ded add condition to update query 2015-07-27 16:09:57 +02:00
Joas Schilling b8561bda9c Add an update script to reset the value
In case encryption was not enabled, we accidently set encrypted = 1 for
files inside mount points, since 8.1.0. This breaks opening the files in
8.1.1 because we fixed the code that checks if a file is encrypted.
In order to fix the file, we need to reset the flag of the file. However,
the flag might be set because the file is in fact encrypted because it was
uploaded at a time where encryption was enabled.

So we can only do this when:
- Current version of ownCloud before the update is 8.1.0 or 8.2.0.(0-2)
- Encryption is disabled
- files_encryption is not known in the app config

If the first two are not the case, we are save. However, if files_encryption
values exist in the config, we might have a false negative here.
Now if there is no file with unencrypted size greater 0, that means there are
no files that are still encrypted with "files_encryption" encryption. So we
can also safely reset the flag here.

If this is not the case, we go with "better save then sorry" and don't change
the flag but write a message to the ownCloud log file.
2015-07-27 16:09:45 +02:00
Joas Schilling c4ddf08b79 Add a unit test for the disabled encryption case 2015-07-27 16:08:19 +02:00
Joas Schilling ef65e519ae Only set is encrypted when encryption is enabled 2015-07-27 16:08:11 +02:00
Bjoern Schiessle 4b41021fc3 get header size before we open the file to avoid locking exception 2015-07-27 14:31:06 +02:00
Thomas Müller 0ef06afc75 Merge pull request #17896 from owncloud/backport-scan-check-path
[stable8.1] check if the user is trying to scan a valid path
2015-07-27 13:40:11 +02:00
Thomas Müller 6dedef7967 Merge pull request #17883 from owncloud/backport-17882
[stable8.1] Allow classes in <h2> tags
2015-07-27 11:56:32 +02:00
Thomas Müller 1c7d737124 Merge pull request #17842 from rullzer/backport_17818
[Stable8.1] assign error message to correct object (backport #17818)
2015-07-27 11:28:15 +02:00
Lukas Reschke 4ee61f3c7a Add unit tests 2015-07-27 11:22:28 +02:00
Robin Appelman c910ac1f47 check if the user is trying to scan a valid path 2015-07-27 11:19:27 +02:00
Morris Jobke 95e44bfc4e Merge pull request #17843 from owncloud/dont-set-share-password-twice-stable8.1
Adding error handling in case setting the password fails
2015-07-27 08:56:47 +02:00
Lukas Reschke ee18718099 Allow classes in <h2> tags
Previously something like `<h2 class="inlineblock"><?php p($l->t('Some title')) ?></h2>` was shown as `<h2 class="inlineblock">Some title` within the sidebar instead as `Some Title` due to the fact that the regex was catching these classes but was not properly running the string replace function.
2015-07-26 23:02:22 +02:00
Thomas Müller 6782c6d22e Merge pull request #17853 from owncloud/sharing-password-policy-stable8.1
Sharing password policy stable8.1
2015-07-24 21:25:41 +02:00
Thomas Müller cef2680c0d Fix PHPDoc on setPassword 2015-07-24 16:49:34 +02:00
Thomas Müller e29158c892 Use a hook to integrate sharing password verification 2015-07-24 16:49:19 +02:00
Thomas Müller a53b7e3240 Adding error handling in case setting the password fails 2015-07-23 16:35:10 +02:00
michag86 74e0b6f1e5 assign error message to correct object
fix for  #17817
2015-07-23 16:17:39 +02:00
Jan-Christoph Borchardt b8e8e3fdaf Merge pull request #17832 from owncloud/improve_apps_template_8.1
[8.1 backport] make sure that there is a space between user- and admin-documentation
2015-07-23 12:29:09 +02:00
Bjoern Schiessle 965fd990fc make sure that there is a space between user- and admin-documentation 2015-07-23 11:00:10 +02:00
C Montero-Luque 3fdd5d9012 8.1.1 beta1 again 2015-07-22 17:05:14 -04:00
C Montero-Luque b4bef0214e 8.1.1 beta1 2015-07-22 16:49:28 -04:00
Thomas Müller ee85d1fd37 Merge pull request #17802 from owncloud/update-sabre-dav-2.1.6
Update SabreDAV to 2.1.6
2015-07-22 21:19:35 +02:00
Björn Schießle 77a37e076f Merge pull request #17816 from owncloud/enc_improved_app_description_8.1
[8.1 backport] improved description for the default encryption module
2015-07-22 21:01:20 +02:00
Bjoern Schiessle a07675e513 improved app description and adjust it to the way the new encryption module works 2015-07-22 16:18:10 +02:00
Joas Schilling 2b63903942 Update SabreDAV to 2.1.6 2015-07-22 12:00:29 +02:00
Joas Schilling 2bc2e974b2 Merge pull request #17752 from owncloud/enc_migration_fix_mountpoint_detection_8.1
[8.1 backport] fix system wide mount point detection on migration
2015-07-21 11:06:09 +02:00
Morris Jobke 2ecba9e5a5 Merge pull request #17762 from owncloud/backport-17723-stable8.1
Backport of #17723 to stable8.1
2015-07-21 00:30:37 +02:00
Morris Jobke dd5b347672 Merge pull request #17704 from owncloud/backport-stable8.1-17255-17526
Backport stable8.1 #17255 #17526
2015-07-21 00:01:36 +02:00
Arthur Schiwon 8be59bf7e0 Backport of #17723 to stable8.1
fix runtime caching in ldap's user manager, fixes #17631

fix indentation. no code changes, whitespace only.
2015-07-20 22:50:10 +02:00
Thomas Müller e92d427dde Merge pull request #17707 from owncloud/backport-fix-closest-stable8.1
fix browser compatibility issue for element.closest
2015-07-20 21:36:22 +02:00
Olivier Paroz 897e99ac58 Fix preview of old file on public upload conflicts 2015-07-20 16:51:28 +02:00
Bjoern Schiessle dc6cc57e11 fix mount point detection 2015-07-20 16:26:07 +02:00
Morris Jobke f1df6cd4ff Merge pull request #17736 from owncloud/backport-allow-update-of-disabled-apps
[stable8.1] Allow upgrade of not enabled apps
2015-07-20 14:24:22 +02:00
Morris Jobke ff8e63c991 Merge pull request #17711 from owncloud/fix_repeated_migration_oc8.1
[8.1 backport] Don't mix up directories if the migration runs multiple times
2015-07-20 13:58:20 +02:00
Thomas Müller d4cc4019fd Allow upgrade of not enabled apps 2015-07-19 12:16:15 +02:00
Björn Schießle 6383bd3744 Merge pull request #17702 from owncloud/enc_detect_legacy_files2_8.1
[8.1 backport] make sure that we always detect legacy files correctlly
2015-07-17 21:39:45 +02:00
Morris Jobke 738e116c00 Merge pull request #17710 from owncloud/stable8.1-s2s-catchremotelockexceptions
[Stable 8.1] s2s catchremotelockexceptions
2015-07-17 18:35:10 +02:00
Bjoern Schiessle 777964ff2b unit tests 2015-07-17 15:30:55 +02:00
Bjoern Schiessle 00a044f885 don't move keys if the key where already moved in a previous migration run 2015-07-17 15:30:43 +02:00
Vincent Petry 335372064e Throw lock exceptions if remote share returned 423 status code 2015-07-17 15:26:40 +02:00
Hendrik Leppelsack 17d7a2893e fix browser compatibility issue for element.closest 2015-07-17 15:08:45 +02:00
Morris Jobke 1b8eb668de Merge pull request #17697 from owncloud/fix-undefined-REMOTE_ADDR-stable8.1
Fixing 'Undefined index: REMOTE_ADDR' - fixes #17460
2015-07-17 14:20:57 +02:00
Arthur Schiwon a3172008c6 LDAP: when checking group for matching filter, also take base DN into consideration. Fixes #17516 2015-07-17 13:42:33 +02:00
Arthur Schiwon b2db982768 Backport of 17255 to stable8.1
ensure groups match filter when using memberOf to read users group, refs #17119

integration test

adjust unit test

tests solidity
2015-07-17 13:41:58 +02:00
Bjoern Schiessle 001dd400a1 set targetIsEncrypted to true if file cache indicates that we try to read a encrypted file 2015-07-17 13:28:58 +02:00
Bjoern Schiessle 5bfbfca266 make sure that we always detect legacy files correctly 2015-07-17 13:28:58 +02:00
Thomas Müller fc6420c290 Fixing 'Undefined index: REMOTE_ADDR' - fixes #17460 2015-07-17 12:35:00 +02:00
Thomas Müller 015d4e3ba3 Merge pull request #17690 from owncloud/stable8.1-repair-ocs-ids
[stable8.1] Add repair step for outdated OCS IDs
2015-07-17 12:02:45 +02:00
Thomas Müller deeacacfae Merge pull request #17678 from owncloud/backport-encryption_migration_improvements-stable8.1
Backport encryption migration improvements stable8.1
2015-07-17 11:58:06 +02:00
Thomas Müller d199fb4e8d Merge pull request #17679 from owncloud/backport-fix_trashbin-stable8.1
only move real files to the trash bin
2015-07-17 11:57:41 +02:00
Lukas Reschke 289fd99333 Prefer OCS Id from database
To be consistent with other logic the app id from the database needs to be prefered. Especially when it comes to be able to replace an outdated OCS id.
2015-07-17 08:33:51 +02:00
Lukas Reschke 579bee03bd Add repair step for outdated OCS IDs
There is the case where OCs IDs might become outdated such as it has been with the calendar and contacts app which refer to the old dummy entry. This means that users with the old OCS id can't update updates as well will receive invalid state flags. (e.g. "experimental" instead of "approved")

To allow instances to properly update the applications in the future we need to migrate the OCS IDs for now manually.
2015-07-16 18:52:27 +02:00
Morris Jobke 816910baf1 Merge pull request #17676 from owncloud/backport-files-scan-user-path-stable8.1
Lock scanner to the given user
2015-07-16 16:00:43 +02:00
Bjoern Schiessle fb452486d7 only move real files to the trash bin 2015-07-16 15:11:12 +02:00
Bjoern Schiessle ae1332e9c9 only create new key pair if both keys are missing 2015-07-16 14:07:26 +02:00
Bjoern Schiessle 9d9ab82a17 only cleanUp the remaining keys if the migration really finished succesfully 2015-07-16 14:07:17 +02:00
Morris Jobke edb15bd493 Merge pull request #17653 from owncloud/backport-17330-share-group-path-exception
[stable8.1] Fix the path for users which have an exception for a group share
2015-07-16 13:19:19 +02:00
Robin Appelman 4c6b26d056 Lock scanner to the given user 2015-07-16 12:14:23 +02:00
Vincent Petry 8e1fca7402 Merge pull request #17658 from owncloud/share-lock-owner-parent-stable81
[8.1] lock parent folders for the owner when locking a shared file as recipient
2015-07-16 11:59:48 +02:00
Vincent Petry db5a574faf Merge pull request #17556 from owncloud/enc_fix_migration_stable8.1
[stable8.1 backport] more secure way to update the database
2015-07-16 11:57:00 +02:00
Morris Jobke 8e0294d0fd Merge pull request #17294 from owncloud/stable8.1-s2s-catchmoreexceptiontypes
[stable8.1] Catch more error codes thrown by federated shares
2015-07-16 11:50:00 +02:00
Morris Jobke 256c57c01e Merge pull request #17337 from owncloud/stable8.1-backport-17077
[stable8.1] fix getting mount points when passing a path to the files:scan command
2015-07-15 14:51:57 +02:00
Robin Appelman db21b0964c only lock the parent folders 2015-07-15 13:43:34 +02:00
Robin Appelman cc45b49de7 lock parent folders for the owner when locking a shared file as recipient 2015-07-15 13:42:59 +02:00
Morris Jobke 90104fc5d8 Merge pull request #17643 from owncloud/stable8.1-s2s-hasupdated-catch405
[stable8.1] Throw StorageNotAvailable if propfind on root failed
2015-07-15 11:11:28 +02:00
Joas Schilling 8f2ce01553 Make sure the owner always has the right path 2015-07-15 10:12:50 +02:00
Vincent Petry 3ade5f5111 Add test case when owner renames shared folder 2015-07-15 10:12:41 +02:00
Vincent Petry 60939ebd2c Add unit test for getUsersSharingFile
This is to test if the user list and paths are correct, even when a
recipient renamed the received shared folder
2015-07-15 10:12:29 +02:00
Joas Schilling c73f938ff4 Fix the path for users which have an exception for a group share 2015-07-15 10:12:16 +02:00
Björn Schießle 06d7480fa2 Merge pull request #17642 from owncloud/stable8.1-backport-17565
[stable8.1] allow remote shares for users with email as usernames
2015-07-14 18:20:45 +02:00
Vincent Petry 48b2a4e034 Throw StorageNotAvailable if propfind on root failed
If PROPFIND fails with 404 or 405 on the remote share root, it means the
storage is not available. Throw StorageNotAvailable is such case.
2015-07-14 17:41:58 +02:00
Felix Böhm 824df7e22d allow remote shares for users with email as usernames 2015-07-14 15:33:12 +02:00
Vincent Petry ab308d246a Merge pull request #17632 from owncloud/stable8.1-backport-17606
[stable8.1] Handle returned null value in app level code
2015-07-14 11:44:06 +02:00
Morris Jobke dc9454ea3c Handle returned null value in app level code
* getApplication on OCSClient can also return null
  this is now handled properly
* fixes #17587
2015-07-14 08:58:28 +02:00
Vincent Petry f920923dd6 Throw storage not available on guzzle error
If the remote server is in maintenance mode, we must throw storage not
available exception instead of not found which might auto-remove the
share.
2015-07-13 18:55:24 +02:00
Thomas Müller 698e2ebc90 Merge pull request #17607 from owncloud/stable8.1-backport-17426
[stable8.1] [config sample] Update info about appstore
2015-07-13 17:40:54 +02:00
Morris Jobke 1548d69762 [config sample] Update info about appstore 2015-07-13 14:52:38 +02:00
Bjoern Schiessle 8466d9d235 more secure way to update the database 2015-07-10 11:49:51 +02:00
Morris Jobke 48b1e9e2a6 Merge pull request #17288 from owncloud/stable8.1-chunk-cleanupgracefulonlock
Stable8.1 chunk cleanupgracefulonlock
2015-07-09 22:31:40 +02:00
Morris Jobke f21eb35431 Merge pull request #17518 from owncloud/stable8.1-backport-config-sample
[stable8.1] backport config sample
2015-07-09 14:30:05 +02:00
Morris Jobke 509725fad9 [config sample] improve RST markup and wording 2015-07-09 10:09:11 +02:00
Morris Jobke 447c60c4e4 refine sample config text 2015-07-09 10:06:06 +02:00
Morris Jobke eb055b0d78 [config sample] merge Miscellaneous & All other options 2015-07-09 10:06:01 +02:00
Morris Jobke d48d59661a Merge pull request #17289 from owncloud/stable8.1-files-cleanuppartfileonlyonce
Stable8.1 files cleanuppartfileonlyonce
2015-07-08 10:34:55 +02:00
Morris Jobke 312d82fe71 Merge pull request #17332 from owncloud/fix-temporary-mountpoint-name-in-activities
Fix the mount point name before creating the activities
2015-07-08 10:33:15 +02:00
Morris Jobke 96b97d1567 Merge pull request #17338 from owncloud/stable8.1-backport-17252
[stable8.1] Only do the description kung-fu on strings - fixes #17028
2015-07-08 10:32:53 +02:00
Morris Jobke 97e49e422f Merge pull request #17366 from owncloud/fix_16740_8.1
[oc8.1] owner is stored as 'uid_owner', not as 'owner' in the oc_share table
2015-07-08 10:32:30 +02:00
Morris Jobke 5d3d44eeb9 Merge pull request #17370 from owncloud/stable8.1-share-onlyshowstaticownerifavailable
[stable8.1] Do not show static share owner if not available
2015-07-08 10:31:33 +02:00
Morris Jobke 51978aa296 Merge pull request #17417 from owncloud/stable8.1-backport-17159
[stable8.1] remove duplicate ID in HTML template for public shares
2015-07-08 10:31:11 +02:00
Morris Jobke 3823713c7c Merge pull request #17429 from owncloud/stable8.1-backport-17088
[stable8.1] [upgrade] add verbosity check and show repair info & steps
2015-07-08 10:29:16 +02:00
Thomas Müller 3854dead08 Merge pull request #17431 from owncloud/stable8.1-update-readme
[stable8.1] Update readme versions
2015-07-07 10:33:36 +02:00
Vincent Petry ce0f391ecd Update README version and bower 2015-07-07 10:22:36 +02:00
Morris Jobke 388409290a [Repair] add repair info for changed collation 2015-07-07 10:16:54 +02:00
Morris Jobke fb85fc619d [upgrade] add verbosity check and show repair info & steps 2015-07-07 10:16:49 +02:00
Morris Jobke d5e90e7239 remove duplicate ID in HTML template for public shares 2015-07-06 13:34:11 +02:00
Vincent Petry c04beb0bad Do not show static share owner if not available
In some corner cases, an outgoing share exists but sharing is not
allowed for the current user. This would cause the file list to break
because the static text could not be rendered as the owner was
undefined.
2015-07-03 11:54:50 +02:00
Bjoern Schiessle 9def2fcf71 call post_addToGroup als for class OC_User because sharing and LDAP are using this class. Minimal approach to fix #16740 2015-07-03 10:36:09 +02:00
Bjoern Schiessle 1427a79643 owner is stored as 'uid_owner', not as 'owner' in the oc_share table 2015-07-03 10:36:01 +02:00
C Montero-Luque 06ec916b11 8.1.0 2015-07-02 18:20:11 -04:00
Lukas Reschke 7d94d963e9 Merge pull request #17356 from owncloud/stable8.1-apps-keepgloballist
[stable8.1] Fix global app list state
2015-07-02 21:37:41 +02:00
Vincent Petry c051c180ab Fix global app list state 2015-07-02 17:31:22 +02:00
Lukas Reschke 9cba7c1793 Merge pull request #17317 from owncloud/stable81-clean-ocsid
[stable8.1] Delete OCS ID from DB if none is specified
2015-07-02 14:15:27 +02:00
Lukas Reschke 16514762bf Merge pull request #17311 from owncloud/stable81-remove-ocs-ids
[stable8.1] Remove OCS IDs
2015-07-02 14:15:19 +02:00
Thomas Müller 2727d9797d Only do the description kung-fu on strings - fixes #17028 2015-07-02 13:16:47 +02:00
Robin Appelman a95b5c3b3e handle invalid results from mount providers 2015-07-02 13:03:09 +02:00
Robin Appelman 1902101923 handle error during setup 2015-07-02 13:03:06 +02:00
Robin Appelman 8a3f54c225 add unit test 2015-07-02 13:03:03 +02:00
Robin Appelman 478fe752e7 fix getting mount points when passing a path to the files:scan command 2015-07-02 13:02:59 +02:00
Joas Schilling ea731c5c66 Use the item name when refering to the unaccepted remote share 2015-07-02 12:35:30 +02:00
Björn Schießle caa3210a59 Merge pull request #17308 from owncloud/stable8.1-backport-17293
Stable8.1 backport 17293
2015-07-02 12:17:56 +02:00
Lukas Reschke 8fe5d4b268 Bump versions 2015-07-02 09:20:25 +02:00
Lukas Reschke 85fc84e3d3 Delete OCS ID from DB if none is specified
If no OCS ID is specified in appinfo.xml and an app update is triggered and a OCS ID is stored in the DB we should clean the value.

Ref https://github.com/owncloud/activity/issues/320#issuecomment-117937748
2015-07-02 09:20:21 +02:00
Lukas Reschke 6008bf975d [stable81] Remove OCS IDs
While making the AppStore ready for 8.1 I also deleted some dummy entries which means that these IDs do not resolve anymore. We should remove them to prevent errors such as https://github.com/owncloud/core/issues/17307

Ref https://github.com/owncloud/activity/issues/320#issuecomment-117691867
2015-07-02 08:47:31 +02:00
Jan-Christoph Borchardt 4abec8b5b9 Revert "fix z-index of share-autocomplete"
This reverts commit 4edf388a38.
2015-07-02 08:24:04 +02:00
Jan-Christoph Borchardt d6cbf8be71 Revert "explicitly set z-index on app-content, fix overlap from navigation"
This reverts commit 71e5bc1803.
2015-07-02 08:23:58 +02:00
Vincent Petry 2d92b5a64e Catch more error codes thrown by federated shares
Most of the time it doesn't make sense to forward Guzzle's
RequestException, so we convert it to StorageNotAvailable instead.

This prevents unpredictable error codes to block access to unrelated
folders needlessly.
2015-07-01 16:23:57 +02:00
Thomas Müller d6b24c7bbc Merge pull request #17266 from owncloud/stable8.1-backport-17224
Stable8.1 backport 17224
2015-07-01 16:12:55 +02:00
Vincent Petry 4aa2eff94c Only delete part file on error if it is really a part file 2015-07-01 15:25:17 +02:00
Vincent Petry 4fff832fa6 Clean up part file only once, not twice on error 2015-07-01 15:25:09 +02:00
Vincent Petry 39e391fd1a Catch cache garbage collection exception on postLogin
Just log the exception instead of preventing access to OC.
2015-07-01 15:19:46 +02:00
Vincent Petry e48afaf334 Test for chunk cache garbage collection 2015-07-01 15:19:43 +02:00
Vincent Petry 18e4fe7add Do not try clearing locked files in cache folder 2015-07-01 15:19:38 +02:00
Morris Jobke c7bc669e00 Merge pull request #17267 from owncloud/stable8.1-backport-17264
Use UTF-8 mode for preg_split and preg_replace
2015-07-01 09:13:48 +02:00
Lukas Reschke 09038697cd Use UTF-8 mode for preg_split and preg_replace
Otherwise a single application with a description containing a non compliant character can break the whole ownCloud appstore. This is for example https://apps.owncloud.com/content/show.php?content=149553

Fixes https://github.com/owncloud/core/issues/17101#issuecomment-117365224
2015-07-01 07:52:40 +02:00
Thomas Müller eb2e8d99cc Avoid namespace clash 2015-07-01 07:48:35 +02:00
Thomas Müller 82493e9789 Fixing content type detection and handle all local printErrorPage calls 2015-07-01 07:48:31 +02:00
Thomas Müller 526a45be18 Adding request specific exception handling - now with WebDAV responses - refs #17192 2015-07-01 07:48:27 +02:00
Thomas Müller 283f8e7e69 Adding exception handling for ServerNotAvailableException - refs #17192 2015-07-01 07:48:23 +02:00
175 changed files with 4049 additions and 1779 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# Version: 8.1.0
# Version: 8.1.2
<IfModule mod_headers.c>
<IfModule mod_fcgid.c>
<IfModule mod_setenvif.c>
+2 -2
View File
@@ -17,7 +17,7 @@ Depencencies:
[![Dependency Status](https://www.versioneye.com/user/projects/54d1f76f3ca0840b190000c0/badge.svg?style=flat)](https://www.versioneye.com/user/projects/54d1f76f3ca0840b190000c0)
### Installation instructions
https://doc.owncloud.org/server/8.0/developer_manual/app/index.html
https://doc.owncloud.org/server/8.1/developer_manual/app/index.html
### Contribution Guidelines
https://owncloud.org/contribute/
@@ -35,4 +35,4 @@ https://www.transifex.com/projects/p/owncloud/
[![Transifex](https://www.transifex.com/projects/p/owncloud/resource/core/chart/image_png)](https://www.transifex.com/projects/p/owncloud/)
For more detailed information about translations:
http://doc.owncloud.org/server/8.0/developer_manual/core/translation.html
http://doc.owncloud.org/server/8.1/developer_manual/core/translation.html
+8 -13
View File
@@ -2,19 +2,14 @@
<info>
<id>encryption</id>
<description>
This application encrypts all files accessed by ownCloud at rest,
wherever they are stored. As an example, with this application
enabled, external cloud based Amazon S3 storage will be encrypted,
protecting this data on storage outside of the control of the Admin.
When this application is enabled for the first time, all files are
encrypted as users log in and are prompted for their password. The
recommended recovery key option enables recovery of files in case
the key is lost.
Note that this app encrypts all files that are touched by ownCloud,
so external storage providers and applications such as SharePoint
will see new files encrypted when they are accessed. Encryption is
based on AES 128 or 256 bit keys. More information is available in
the Encryption documentation
In order to use this encryption module you need to enable server-side
encryption in the admin settings. Once enabled this module will encrypt
all your files transparently. The encryption is based on AES 256 keys.
The module won't touch existing files, only new files will be encrypted
after server-side encryption was enabled. It is also not possible to
disable the encryption again and switch back to a unencrypted system.
Please read the documentation to know all implications before you decide
to enable server-side encryption.
</description>
<name>Default encryption module</name>
<license>AGPL</license>
+2 -1
View File
@@ -26,4 +26,5 @@ $userManager = OC::$server->getUserManager();
$view = new \OC\Files\View();
$config = \OC::$server->getConfig();
$connection = \OC::$server->getDatabaseConnection();
$application->add(new MigrateKeys($userManager, $view, $connection, $config));
$logger = \OC::$server->getLogger();
$application->add(new MigrateKeys($userManager, $view, $connection, $config, $logger));
+10 -2
View File
@@ -27,6 +27,7 @@ use OC\Files\View;
use OC\User\Manager;
use OCA\Encryption\Migration;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IUserBackend;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -44,22 +45,27 @@ class MigrateKeys extends Command {
private $connection;
/** @var IConfig */
private $config;
/** @var ILogger */
private $logger;
/**
* @param Manager $userManager
* @param View $view
* @param Connection $connection
* @param IConfig $config
* @param ILogger $logger
*/
public function __construct(Manager $userManager,
View $view,
Connection $connection,
IConfig $config) {
IConfig $config,
ILogger $logger) {
$this->userManager = $userManager;
$this->view = $view;
$this->connection = $connection;
$this->config = $config;
$this->logger = $logger;
parent::__construct();
}
@@ -77,7 +83,7 @@ class MigrateKeys extends Command {
protected function execute(InputInterface $input, OutputInterface $output) {
// perform system reorganization
$migration = new Migration($this->config, $this->view, $this->connection);
$migration = new Migration($this->config, $this->view, $this->connection, $this->logger);
$users = $input->getArgument('user_id');
if (!empty($users)) {
@@ -115,5 +121,7 @@ class MigrateKeys extends Command {
}
}
$migration->finalCleanUp();
}
}
+23 -6
View File
@@ -406,19 +406,36 @@ class KeyManager {
}
/**
* @param $userId
* check if user has a private and a public key
*
* @param string $userId
* @return bool
* @throws PrivateKeyMissingException
* @throws PublicKeyMissingException
*/
public function userHasKeys($userId) {
$privateKey = $publicKey = true;
try {
$this->getPrivateKey($userId);
$this->getPublicKey($userId);
} catch (PrivateKeyMissingException $e) {
return false;
} catch (PublicKeyMissingException $e) {
return false;
$privateKey = false;
$exception = $e;
}
try {
$this->getPublicKey($userId);
} catch (PublicKeyMissingException $e) {
$publicKey = false;
$exception = $e;
}
if ($privateKey && $publicKey) {
return true;
} elseif (!$privateKey && !$publicKey) {
return false;
} else {
throw $exception;
}
return true;
}
/**
+105 -35
View File
@@ -26,6 +26,7 @@ namespace OCA\Encryption;
use OC\DB\Connection;
use OC\Files\View;
use OCP\IConfig;
use OCP\ILogger;
class Migration {
@@ -36,21 +37,28 @@ class Migration {
private $connection;
/** @var IConfig */
private $config;
/** @var ILogger */
private $logger;
/** @var string*/
protected $installedVersion;
/**
* @param IConfig $config
* @param View $view
* @param Connection $connection
* @param ILogger $logger
*/
public function __construct(IConfig $config, View $view, Connection $connection) {
public function __construct(IConfig $config, View $view, Connection $connection, ILogger $logger) {
$this->view = $view;
$this->view->getUpdater()->disable();
$this->connection = $connection;
$this->moduleId = \OCA\Encryption\Crypto\Encryption::ID;
$this->config = $config;
$this->logger = $logger;
$this->installedVersion = $this->config->getAppValue('files_encryption', 'installed_version', '-1');
}
public function __destruct() {
public function finalCleanUp() {
$this->view->deleteAll('files_encryption/public_keys');
$this->updateFileCache();
$this->config->deleteAppValue('files_encryption', 'installed_version');
@@ -60,12 +68,16 @@ class Migration {
* update file cache, copy unencrypted_size to the 'size' column
*/
private function updateFileCache() {
$query = $this->connection->createQueryBuilder();
$query->update('`*PREFIX*filecache`')
->set('`size`', '`unencrypted_size`')
->where($query->expr()->eq('`encrypted`', ':encrypted'))
->setParameter('encrypted', 1);
$query->execute();
// make sure that we don't update the file cache multiple times
// only update during the first run
if ($this->installedVersion !== '-1') {
$query = $this->connection->createQueryBuilder();
$query->update('`*PREFIX*filecache`')
->set('`size`', '`unencrypted_size`')
->where($query->expr()->eq('`encrypted`', ':encrypted'))
->setParameter('encrypted', 1);
$query->execute();
}
}
/**
@@ -138,27 +150,43 @@ class Migration {
*/
public function updateDB() {
// make sure that we don't update the file cache multiple times
// only update during the first run
if ($this->installedVersion === '-1') {
return;
}
// delete left-over from old encryption which is no longer needed
$this->config->deleteAppValue('files_encryption', 'ocsid');
$this->config->deleteAppValue('files_encryption', 'types');
$this->config->deleteAppValue('files_encryption', 'enabled');
$oldAppValues = $this->connection->createQueryBuilder();
$oldAppValues->select('*')
->from('`*PREFIX*appconfig`')
->where($oldAppValues->expr()->eq('`appid`', ':appid'))
->setParameter('appid', 'files_encryption');
$appSettings = $oldAppValues->execute();
$query = $this->connection->createQueryBuilder();
$query->update('`*PREFIX*appconfig`')
->set('`appid`', ':newappid')
->where($query->expr()->eq('`appid`', ':oldappid'))
->setParameter('oldappid', 'files_encryption')
->setParameter('newappid', 'encryption');
$query->execute();
while ($row = $appSettings->fetch()) {
// 'installed_version' gets deleted at the end of the migration process
if ($row['configkey'] !== 'installed_version' ) {
$this->config->setAppValue('encryption', $row['configkey'], $row['configvalue']);
$this->config->deleteAppValue('files_encryption', $row['configkey']);
}
}
$query = $this->connection->createQueryBuilder();
$query->update('`*PREFIX*preferences`')
->set('`appid`', ':newappid')
->where($query->expr()->eq('`appid`', ':oldappid'))
->setParameter('oldappid', 'files_encryption')
->setParameter('newappid', 'encryption');
$query->execute();
$oldPreferences = $this->connection->createQueryBuilder();
$oldPreferences->select('*')
->from('`*PREFIX*preferences`')
->where($oldPreferences->expr()->eq('`appid`', ':appid'))
->setParameter('appid', 'files_encryption');
$preferenceSettings = $oldPreferences->execute();
while ($row = $preferenceSettings->fetch()) {
$this->config->setUserValue($row['userid'], 'encryption', $row['configkey'], $row['configvalue']);
$this->config->deleteUserValue($row['userid'], 'files_encryption', $row['configkey']);
}
}
/**
@@ -224,9 +252,10 @@ class Migration {
private function renameUsersPrivateKey($user) {
$oldPrivateKey = $user . '/files_encryption/' . $user . '.privateKey';
$newPrivateKey = $user . '/files_encryption/' . $this->moduleId . '/' . $user . '.privateKey';
$this->createPathForKeys(dirname($newPrivateKey));
$this->view->rename($oldPrivateKey, $newPrivateKey);
if ($this->view->file_exists($oldPrivateKey)) {
$this->createPathForKeys(dirname($newPrivateKey));
$this->view->rename($oldPrivateKey, $newPrivateKey);
}
}
/**
@@ -237,9 +266,10 @@ class Migration {
private function renameUsersPublicKey($user) {
$oldPublicKey = '/files_encryption/public_keys/' . $user . '.publicKey';
$newPublicKey = $user . '/files_encryption/' . $this->moduleId . '/' . $user . '.publicKey';
$this->createPathForKeys(dirname($newPublicKey));
$this->view->rename($oldPublicKey, $newPublicKey);
if ($this->view->file_exists($oldPublicKey)) {
$this->createPathForKeys(dirname($newPublicKey));
$this->view->rename($oldPublicKey, $newPublicKey);
}
}
/**
@@ -251,6 +281,11 @@ class Migration {
*/
private function renameFileKeys($user, $path, $trash = false) {
if ($this->view->is_dir($user . '/' . $path) === false) {
$this->logger->info('Skip dir /' . $user . '/' . $path . ': does not exist');
return;
}
$dh = $this->view->opendir($user . '/' . $path);
if (is_resource($dh)) {
@@ -260,8 +295,15 @@ class Migration {
$this->renameFileKeys($user, $path . '/' . $file, $trash);
} else {
$target = $this->getTargetDir($user, $path, $file, $trash);
$this->createPathForKeys(dirname($target));
$this->view->rename($user . '/' . $path . '/' . $file, $target);
if ($target !== false) {
$this->createPathForKeys(dirname($target));
$this->view->rename($user . '/' . $path . '/' . $file, $target);
} else {
$this->logger->warning(
'did not move key "' . $file
. '" could not find the corresponding file in /data/' . $user . '/files.'
. 'Most likely the key was already moved in a previous migration run and is already on the right place.');
}
}
}
}
@@ -269,23 +311,51 @@ class Migration {
}
}
/**
* get system mount points
* wrap static method so that it can be mocked for testing
*
* @internal
* @return array
*/
protected function getSystemMountPoints() {
return \OC_Mount_Config::getSystemMountPoints();
}
/**
* generate target directory
*
* @param string $user
* @param string $filePath
* @param string $keyPath
* @param string $filename
* @param bool $trash
* @return string
*/
private function getTargetDir($user, $filePath, $filename, $trash) {
private function getTargetDir($user, $keyPath, $filename, $trash) {
if ($trash) {
$targetDir = $user . '/files_encryption/keys/files_trashbin/' . substr($filePath, strlen('/files_trashbin/keys/')) . '/' . $this->moduleId . '/' . $filename;
$filePath = substr($keyPath, strlen('/files_trashbin/keys/'));
$targetDir = $user . '/files_encryption/keys/files_trashbin/' . $filePath . '/' . $this->moduleId . '/' . $filename;
} else {
$targetDir = $user . '/files_encryption/keys/files/' . substr($filePath, strlen('/files_encryption/keys/')) . '/' . $this->moduleId . '/' . $filename;
$filePath = substr($keyPath, strlen('/files_encryption/keys/'));
$targetDir = $user . '/files_encryption/keys/files/' . $filePath . '/' . $this->moduleId . '/' . $filename;
}
return $targetDir;
if ($user === '') {
// for system wide mounts we need to check if the mount point really exists
$normalized = \OC\Files\Filesystem::normalizePath($filePath);
$systemMountPoints = $this->getSystemMountPoints();
foreach ($systemMountPoints as $mountPoint) {
$normalizedMountPoint = \OC\Files\Filesystem::normalizePath($mountPoint['mountpoint']) . '/';
if (strpos($normalized, $normalizedMountPoint) === 0)
return $targetDir;
}
} else if ($trash === false && $this->view->file_exists('/' . $user. '/files/' . $filePath)) {
return $targetDir;
} else if ($trash === true && $this->view->file_exists('/' . $user. '/files_trashbin/' . $filePath)) {
return $targetDir;
}
return false;
}
/**
+47 -3
View File
@@ -182,18 +182,62 @@ class KeyManagerTest extends TestCase {
);
}
public function testUserHasKeys() {
/**
* @dataProvider dataTestUserHasKeys
*/
public function testUserHasKeys($key, $expected) {
$this->keyStorageMock->expects($this->exactly(2))
->method('getUserKey')
->with($this->equalTo($this->userId), $this->anything())
->willReturn('key');
->willReturn($key);
$this->assertTrue(
$this->assertSame($expected,
$this->instance->userHasKeys($this->userId)
);
}
public function dataTestUserHasKeys() {
return [
['key', true],
['', false]
];
}
/**
* @expectedException \OCA\Encryption\Exceptions\PrivateKeyMissingException
*/
public function testUserHasKeysMissingPrivateKey() {
$this->keyStorageMock->expects($this->exactly(2))
->method('getUserKey')
->willReturnCallback(function ($uid, $keyID, $encryptionModuleId) {
if ($keyID=== 'privateKey') {
return '';
}
return 'key';
});
$this->instance->userHasKeys($this->userId);
}
/**
* @expectedException \OCA\Encryption\Exceptions\PublicKeyMissingException
*/
public function testUserHasKeysMissingPublicKey() {
$this->keyStorageMock->expects($this->exactly(2))
->method('getUserKey')
->willReturnCallback(function ($uid, $keyID, $encryptionModuleId){
if ($keyID === 'publicKey') {
return '';
}
return 'key';
});
$this->instance->userHasKeys($this->userId);
}
public function testInit() {
$this->keyStorageMock->expects($this->any())
->method('getUserKey')
+179 -4
View File
@@ -24,6 +24,7 @@
namespace OCA\Encryption\Tests;
use OCA\Encryption\Migration;
use OCP\ILogger;
class MigrationTest extends \Test\TestCase {
@@ -37,6 +38,9 @@ class MigrationTest extends \Test\TestCase {
private $recovery_key_id = 'recovery_key_id';
private $moduleId;
/** @var PHPUnit_Framework_MockObject_MockObject | ILogger */
private $logger;
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
\OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo');
@@ -53,6 +57,7 @@ class MigrationTest extends \Test\TestCase {
public function setUp() {
$this->logger = $this->getMockBuilder('\OCP\ILogger')->disableOriginalConstructor()->getMock();
$this->view = new \OC\Files\View();
$this->moduleId = \OCA\Encryption\Crypto\Encryption::ID;
}
@@ -100,6 +105,17 @@ class MigrationTest extends \Test\TestCase {
$this->view->file_put_contents($uid . '/files_encryption/keys/folder2/file.2.1/fileKey' , 'data');
}
protected function createDummyFiles($uid) {
$this->view->mkdir($uid . '/files/folder1/folder2/folder3/file3');
$this->view->mkdir($uid . '/files/folder1/folder2/file2');
$this->view->mkdir($uid . '/files/folder1/file.1');
$this->view->mkdir($uid . '/files/folder2/file.2.1');
$this->view->file_put_contents($uid . '/files/folder1/folder2/folder3/file3/fileKey' , 'data');
$this->view->file_put_contents($uid . '/files/folder1/folder2/file2/fileKey' , 'data');
$this->view->file_put_contents($uid . '/files/folder1/file.1/fileKey' , 'data');
$this->view->file_put_contents($uid . '/files/folder2/file.2.1/fileKey' , 'data');
}
protected function createDummyFilesInTrash($uid) {
$this->view->mkdir($uid . '/files_trashbin/keys/file1.d5457864');
$this->view->mkdir($uid . '/files_trashbin/keys/folder1.d7437648723/file2');
@@ -109,6 +125,11 @@ class MigrationTest extends \Test\TestCase {
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/fileKey' , 'data');
$this->view->file_put_contents($uid . '/files_trashbin/keys/folder1.d7437648723/file2/fileKey' , 'data');
// create the files itself
$this->view->mkdir($uid . '/files_trashbin/folder1.d7437648723');
$this->view->file_put_contents($uid . '/files_trashbin/file1.d5457864' , 'data');
$this->view->file_put_contents($uid . '/files_trashbin/folder1.d7437648723/file2' , 'data');
}
protected function createDummySystemWideKeys() {
@@ -118,7 +139,6 @@ class MigrationTest extends \Test\TestCase {
$this->view->file_put_contents('files_encryption/systemwide_2.privateKey', 'data');
$this->view->file_put_contents('files_encryption/public_keys/systemwide_1.publicKey', 'data');
$this->view->file_put_contents('files_encryption/public_keys/systemwide_2.publicKey', 'data');
}
public function testMigrateToNewFolderStructure() {
@@ -134,6 +154,10 @@ class MigrationTest extends \Test\TestCase {
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER2);
$this->createDummyFileKeys(self::TEST_ENCRYPTION_MIGRATION_USER3);
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER1);
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER2);
$this->createDummyFiles(self::TEST_ENCRYPTION_MIGRATION_USER3);
$this->createDummyFilesInTrash(self::TEST_ENCRYPTION_MIGRATION_USER2);
// no user for system wide mount points
@@ -142,7 +166,21 @@ class MigrationTest extends \Test\TestCase {
$this->createDummySystemWideKeys();
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
$m = $this->getMockBuilder('OCA\Encryption\Migration')
->setConstructorArgs(
[
\OC::$server->getConfig(),
new \OC\Files\View(),
\OC::$server->getDatabaseConnection(),
$this->logger
]
)->setMethods(['getSystemMountPoints'])->getMock();
$m->expects($this->any())->method('getSystemMountPoints')
->willReturn([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']]);
$m->reorganizeFolderStructure();
// even if it runs twice folder should always move only once
$m->reorganizeFolderStructure();
$this->assertTrue(
@@ -242,6 +280,12 @@ class MigrationTest extends \Test\TestCase {
$config->setAppValue('files_encryption', 'recoveryAdminEnabled', '1');
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'files_encryption', 'recoverKeyEnabled', '1');
//$this->invokePrivate($config, 'cache', [[]]);
$cache = $this->invokePrivate(\OC::$server->getAppConfig(), 'cache');
unset($cache['encryption']);
unset($cache['files_encryption']);
$this->invokePrivate(\OC::$server->getAppConfig(), 'cache', [$cache]);
// delete default values set by the encryption app during initialization
/** @var \OC\DB\Connection $connection */
@@ -261,7 +305,8 @@ class MigrationTest extends \Test\TestCase {
public function testUpdateDB() {
$this->prepareDB();
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger);
$this->invokePrivate($m, 'installedVersion', ['0.7']);
$m->updateDB();
$this->verifyDB('`*PREFIX*appconfig`', 'files_encryption', 0);
@@ -271,6 +316,59 @@ class MigrationTest extends \Test\TestCase {
}
/**
* test update db if the db already contain some existing new values
*/
public function testUpdateDBExistingNewConfig() {
$this->prepareDB();
$config = \OC::$server->getConfig();
$config->setAppValue('encryption', 'publicShareKeyId', 'wrong_share_id');
$config->setUserValue(self::TEST_ENCRYPTION_MIGRATION_USER1, 'encryption', 'recoverKeyEnabled', '9');
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger);
$this->invokePrivate($m, 'installedVersion', ['0.7']);
$m->updateDB();
$this->verifyDB('`*PREFIX*appconfig`', 'files_encryption', 0);
$this->verifyDB('`*PREFIX*preferences`', 'files_encryption', 0);
$this->verifyDB('`*PREFIX*appconfig`', 'encryption', 3);
$this->verifyDB('`*PREFIX*preferences`', 'encryption', 1);
// check if the existing values where overwritten correctly
/** @var \OC\DB\Connection $connection */
$connection = \OC::$server->getDatabaseConnection();
$query = $connection->createQueryBuilder();
$query->select('`configvalue`')
->from('`*PREFIX*appconfig`')
->where($query->expr()->andX(
$query->expr()->eq('`appid`', ':appid'),
$query->expr()->eq('`configkey`', ':configkey')
))
->setParameter('appid', 'encryption')
->setParameter('configkey', 'publicShareKeyId');
$result = $query->execute();
$value = $result->fetch();
$this->assertTrue(isset($value['configvalue']));
$this->assertSame('share_id', $value['configvalue']);
$query = $connection->createQueryBuilder();
$query->select('`configvalue`')
->from('`*PREFIX*preferences`')
->where($query->expr()->andX(
$query->expr()->eq('`appid`', ':appid'),
$query->expr()->eq('`configkey`', ':configkey'),
$query->expr()->eq('`userid`', ':userid')
))
->setParameter('appid', 'encryption')
->setParameter('configkey', 'recoverKeyEnabled')
->setParameter('userid', self::TEST_ENCRYPTION_MIGRATION_USER1);
$result = $query->execute();
$value = $result->fetch();
$this->assertTrue(isset($value['configvalue']));
$this->assertSame('1', $value['configvalue']);
}
public function verifyDB($table, $appid, $expected) {
/** @var \OC\DB\Connection $connection */
$connection = \OC::$server->getDatabaseConnection();
@@ -291,7 +389,8 @@ class MigrationTest extends \Test\TestCase {
*/
public function testUpdateFileCache() {
$this->prepareFileCache();
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection());
$m = new Migration(\OC::$server->getConfig(), new \OC\Files\View(), \OC::$server->getDatabaseConnection(), $this->logger);
$this->invokePrivate($m, 'installedVersion', ['0.7']);
self::invokePrivate($m, 'updateFileCache');
// check results
@@ -353,4 +452,80 @@ class MigrationTest extends \Test\TestCase {
$this->assertSame(19, count($result));
}
/**
* @dataProvider dataTestGetTargetDir
*/
public function testGetTargetDir($user, $keyPath, $filename, $trash, $systemMounts, $expected) {
$updater = $this->getMockBuilder('\OC\Files\Cache\Updater')
->disableOriginalConstructor()->getMock();
$view = $this->getMockBuilder('\OC\Files\View')
->disableOriginalConstructor()->getMock();
$view->expects($this->any())->method('file_exists')->willReturn(true);
$view->expects($this->any())->method('getUpdater')->willReturn($updater);
$m = $this->getMockBuilder('OCA\Encryption\Migration')
->setConstructorArgs(
[
\OC::$server->getConfig(),
$view,
\OC::$server->getDatabaseConnection(),
$this->logger
]
)->setMethods(['getSystemMountPoints'])->getMock();
$m->expects($this->any())->method('getSystemMountPoints')
->willReturn($systemMounts);
$this->assertSame($expected,
$this->invokePrivate($m, 'getTargetDir', [$user, $keyPath, $filename, $trash])
);
}
public function dataTestGetTargetDir() {
return [
[
'user1',
'/files_encryption/keys/foo/bar.txt',
'user1.shareKey',
false,
[],
'user1/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey'
],
[
'user1',
'/files_trashbin/keys/foo/bar.txt',
'user1.shareKey',
true,
[],
'user1/files_encryption/keys/files_trashbin/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey'
],
[
'',
'/files_encryption/keys/foo/bar.txt',
'user1.shareKey',
false,
[['mountpoint' => 'foo']],
'/files_encryption/keys/files/foo/bar.txt/OC_DEFAULT_MODULE/user1.shareKey'
],
[
'',
'/files_encryption/keys/foo/bar.txt',
'user1.shareKey',
false,
[['mountpoint' => 'foobar']],
false
],
[
'',
'/files_encryption/keys/foobar/bar.txt',
'user1.shareKey',
false,
[['mountpoint' => 'foo']],
false
]
];
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
/**
* @author Björn Schießle <schiessle@owncloud.com>
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
$installedVersion = \OC::$server->getConfig()->getAppValue('files', 'installed_version');
$ocVersion = explode('.', \OC::$server->getSystemConfig()->getValue('version'));
/**
* In case encryption was not enabled, we accidently set encrypted = 1 for
* files inside mount points, since 8.1.0. This breaks opening the files in
* 8.1.1 because we fixed the code that checks if a file is encrypted.
* In order to fix the file, we need to reset the flag of the file. However,
* the flag might be set because the file is in fact encrypted because it was
* uploaded at a time where encryption was enabled.
*
* So we can only do this when:
* - Current version of ownCloud before the update is 8.1.0 or 8.2.0.(0-2)
* - Encryption is disabled
* - files_encryption is not known in the app config
*
* If the first two are not the case, we are save. However, if files_encryption
* values exist in the config, we might have a false negative here.
* Now if there is no file with unencrypted size greater 0, that means there are
* no files that are still encrypted with "files_encryption" encryption. So we
* can also safely reset the flag here.
*
* If this is not the case, we go with "better save then sorry" and don't change
* the flag but write a message to the ownCloud log file.
*/
/**
* @param \OCP\IDBConnection $conn
*/
function owncloud_reset_encrypted_flag(\OCP\IDBConnection $conn) {
$conn->executeUpdate('UPDATE `*PREFIX*filecache` SET `encrypted` = 0 WHERE `encrypted` = 1');
}
// Current version of ownCloud before the update is 8.1.0 or 8.2.0.(0-2)
if ($installedVersion === '1.1.9' && (
// 8.1.0.x
(((int) $ocVersion[0]) === 8 && ((int) $ocVersion[1]) === 1 && ((int) $ocVersion[2]) === 0)
||
// < 8.1.1.1
(((int) $ocVersion[0]) === 8 && ((int) $ocVersion[1]) === 1 && ((int) $ocVersion[2]) === 1 && ((int) $ocVersion[3]) < 1)
)) {
// Encryption is not enabled
if (!\OC::$server->getEncryptionManager()->isEnabled()) {
$conn = \OC::$server->getDatabaseConnection();
// Old encryption is not known in app config
$oldEncryption = \OC::$server->getConfig()->getAppKeys('files_encryption');
if (empty($oldEncryption)) {
owncloud_reset_encrypted_flag($conn);
} else {
$query = $conn->prepare('SELECT * FROM `*PREFIX*filecache` WHERE `encrypted` = 1 AND `unencrypted_size` > 0', 1);
$query->execute();
$empty = $query->fetch();
if (empty($empty)) {
owncloud_reset_encrypted_flag($conn);
} else {
/**
* Sorry in case you are a false positive, but we are not 100% that
* you don't have any encrypted files anymore, so we can not reset
* the value safely
*/
\OC::$server->getLogger()->warning(
'If you have a problem with files not being accessible and '
. 'you are not using encryption, please have a look at the following'
. 'issue: {issue}',
[
'issue' => 'https://github.com/owncloud/core/issues/17846',
]
);
}
}
}
}
+1 -1
View File
@@ -1 +1 @@
1.1.9
1.1.10
+5 -4
View File
@@ -92,10 +92,10 @@ class Scan extends Command {
}
protected function execute(InputInterface $input, OutputInterface $output) {
$path = $input->getOption('path');
if ($path) {
$path = '/'.trim($path, '/');
list (, $user, ) = explode('/', $path, 3);
$inputPath = $input->getOption('path');
if ($inputPath) {
$inputPath = '/' . trim($inputPath, '/');
list (, $user,) = explode('/', $inputPath, 3);
$users = array($user);
} else if ($input->getOption('all')) {
$users = $this->userManager->search('');
@@ -114,6 +114,7 @@ class Scan extends Command {
if (is_object($user)) {
$user = $user->getUID();
}
$path = $inputPath ? $inputPath : '/' . $user;
if ($this->userManager->userExists($user)) {
$this->scanFiles($user, $path, $quiet, $output);
} else {
+1
View File
@@ -1659,6 +1659,7 @@
}
this.$table.addClass('hidden');
this.$el.find('#emptycontent').addClass('hidden');
$mask = $('<div class="mask transparent"></div>');
+2 -3
View File
@@ -116,7 +116,7 @@
ownerDisplayName = $('#ownerDisplayName').val();
if (usedSpacePercent > 98) {
if (owner !== oc_current_user) {
OC.Notification.show(t('files', 'Storage of {owner} is full, files can not be updated or synced anymore!',
OC.Notification.showTemporary(t('files', 'Storage of {owner} is full, files can not be updated or synced anymore!',
{ owner: ownerDisplayName }));
return;
}
@@ -125,7 +125,7 @@
}
if (usedSpacePercent > 90) {
if (owner !== oc_current_user) {
OC.Notification.show(t('files', 'Storage of {owner} is almost full ({usedSpacePercent}%)',
OC.Notification.showTemporary(t('files', 'Storage of {owner} is almost full ({usedSpacePercent}%)',
{ usedSpacePercent: usedSpacePercent, owner: ownerDisplayName }));
return;
}
@@ -239,7 +239,6 @@
// display storage warnings
setTimeout(Files.displayStorageWarnings, 100);
OC.Notification.setDefault(Files.displayStorageWarnings);
// only possible at the moment if user is logged in or the files app is loaded
if (OC.currentUser && OCA.Files.App) {
+10 -9
View File
@@ -136,25 +136,26 @@ class Activity implements IExtension {
return false;
}
$l = $this->getL10N($languageCode);
switch ($text) {
case 'created_self':
return (string) $this->l->t('You created %1$s', $params);
return (string) $l->t('You created %1$s', $params);
case 'created_by':
return (string) $this->l->t('%2$s created %1$s', $params);
return (string) $l->t('%2$s created %1$s', $params);
case 'created_public':
return (string) $this->l->t('%1$s was created in a public folder', $params);
return (string) $l->t('%1$s was created in a public folder', $params);
case 'changed_self':
return (string) $this->l->t('You changed %1$s', $params);
return (string) $l->t('You changed %1$s', $params);
case 'changed_by':
return (string) $this->l->t('%2$s changed %1$s', $params);
return (string) $l->t('%2$s changed %1$s', $params);
case 'deleted_self':
return (string) $this->l->t('You deleted %1$s', $params);
return (string) $l->t('You deleted %1$s', $params);
case 'deleted_by':
return (string) $this->l->t('%2$s deleted %1$s', $params);
return (string) $l->t('%2$s deleted %1$s', $params);
case 'restored_self':
return (string) $this->l->t('You restored %1$s', $params);
return (string) $l->t('You restored %1$s', $params);
case 'restored_by':
return (string) $this->l->t('%2$s restored %1$s', $params);
return (string) $l->t('%2$s restored %1$s', $params);
default:
return false;
+44 -1
View File
@@ -42,6 +42,9 @@ class ActivityTest extends TestCase {
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $activityHelper;
/** @var \PHPUnit_Framework_MockObject_MockObject */
protected $l10nFactory;
/** @var \OCA\Files\Activity */
protected $activityExtension;
@@ -67,8 +70,28 @@ class ActivityTest extends TestCase {
$this->config
);
$this->l10nFactory = $this->getMockBuilder('OC\L10N\Factory')
->disableOriginalConstructor()
->getMock();
$deL10n = $this->getMockBuilder('OC_L10N')
->disableOriginalConstructor()
->getMock();
$deL10n->expects($this->any())
->method('t')
->willReturnCallback(function ($argument) {
return 'translate(' . $argument . ')';
});
$this->l10nFactory->expects($this->any())
->method('get')
->willReturnMap([
['files', null, new \OC_L10N('files', 'en')],
['files', 'en', new \OC_L10N('files', 'en')],
['files', 'de', $deL10n],
]);
$this->activityExtension = $activityExtension = new Activity(
new \OC\L10N\Factory(),
$this->l10nFactory,
$this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
$this->activityManager,
$this->activityHelper,
@@ -111,6 +134,26 @@ class ActivityTest extends TestCase {
$this->activityExtension->translate('files_sharing', '', [], false, false, 'en'),
'Asserting that no translations are set for files_sharing'
);
// Test english
$this->assertNotFalse(
$this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'en'),
'Asserting that translations are set for files.deleted_self'
);
$this->assertStringStartsWith(
'You deleted ',
$this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'en')
);
// Test translation
$this->assertNotFalse(
$this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'de'),
'Asserting that translations are set for files.deleted_self'
);
$this->assertStringStartsWith(
'translate(You deleted ',
$this->activityExtension->translate('files', 'deleted_self', ['file'], false, false, 'de')
);
}
public function testGetSpecialParameterList() {
+3
View File
@@ -1 +1,4 @@
example.php
icewind/smb/tests
icewind/smb/install_libsmbclient.sh
icewind/smb/.travis.yml
+1 -1
View File
@@ -6,7 +6,7 @@
"vendor-dir": "."
},
"require": {
"icewind/smb": "1.0.1",
"icewind/smb": "1.0.4",
"icewind/streams": "0.2"
}
}
+7 -7
View File
@@ -1,23 +1,23 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "7b46d64e33feb600c5f0ec830b211e6f",
"hash": "5c612406bc1235075305b09a5d6996a9",
"packages": [
{
"name": "icewind/smb",
"version": "v1.0.1",
"version": "v1.0.4",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/SMB.git",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476"
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/8041bc1960bf2da94e60b88b34e5c78300eac476",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/9277bd20262a01b38a33cc7356e98055f2262d32",
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32",
"shasum": ""
},
"require": {
@@ -45,7 +45,7 @@
}
],
"description": "php wrapper for smbclient and libsmbclient-php",
"time": "2015-04-20 11:16:24"
"time": "2015-08-17 14:20:38"
},
{
"name": "icewind/streams",
+6 -6
View File
@@ -43,17 +43,17 @@
},
{
"name": "icewind/smb",
"version": "v1.0.1",
"version_normalized": "1.0.1.0",
"version": "v1.0.4",
"version_normalized": "1.0.4.0",
"source": {
"type": "git",
"url": "https://github.com/icewind1991/SMB.git",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476"
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/8041bc1960bf2da94e60b88b34e5c78300eac476",
"reference": "8041bc1960bf2da94e60b88b34e5c78300eac476",
"url": "https://api.github.com/repos/icewind1991/SMB/zipball/9277bd20262a01b38a33cc7356e98055f2262d32",
"reference": "9277bd20262a01b38a33cc7356e98055f2262d32",
"shasum": ""
},
"require": {
@@ -63,7 +63,7 @@
"require-dev": {
"satooshi/php-coveralls": "dev-master"
},
"time": "2015-04-20 11:16:24",
"time": "2015-08-17 14:20:38",
"type": "library",
"installation-source": "source",
"autoload": {
-50
View File
@@ -1,50 +0,0 @@
language: php
php:
- 5.3
- 5.4
- 5.5
env:
global:
- CURRENT_DIR=`pwd`
before_install:
- pass=$(perl -e 'print crypt("test", "password")')
- sudo useradd -m -p $pass test
- sudo apt-get update -qq
- sudo apt-get install samba smbclient libsmbclient-dev libsmbclient
- wget -O /tmp/libsmbclient-php.zip https://github.com/eduardok/libsmbclient-php/archive/master.zip
- unzip /tmp/libsmbclient-php.zip -d /tmp
- cd /tmp/libsmbclient-php-master
- phpize && ./configure && make && sudo make install
- echo 'extension="libsmbclient.so"' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- cd $CURRENT_DIR
- chmod go+w $HOME
- printf "%s\n%s\n" test test|sudo smbpasswd -s test
- sudo mkdir /home/test/test
- sudo chown test /home/test/test
- |
echo "[test]
comment = test
path = /home/test
guest ok = yes
writeable = yes
map archive = yes
map system = yes
map hidden = yes
create mask = 0777
inherit permissions = yes" | sudo tee -a /etc/samba/smb.conf
- sudo service smbd restart
- testparm -s
install:
- composer install --dev --no-interaction
script:
- mkdir -p build/logs
- cd tests
- phpunit --coverage-clover ../build/logs/clover.xml --configuration phpunit.xml
after_script:
- cd $CURRENT_DIR
- php vendor/bin/coveralls -v
@@ -0,0 +1,26 @@
<?php
/**
* Copyright (c) 2015 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB;
use Icewind\SMB\Exception\InvalidPathException;
abstract class AbstractShare implements IShare {
private $forbiddenCharacters;
public function __construct() {
$this->forbiddenCharacters = array('?', '<', '>', ':', '*', '|', '"', chr(0), "\n", "\r");
}
protected function verifyPath($path) {
foreach ($this->forbiddenCharacters as $char) {
if (strpos($path, $char) !== false) {
throw new InvalidPathException('Invalid path, "' . $char . '" is not allowed');
}
}
}
}
+19 -6
View File
@@ -8,8 +8,10 @@
namespace Icewind\SMB;
use Icewind\SMB\Exception\AuthenticationException;
use Icewind\SMB\Exception\ConnectException;
use Icewind\SMB\Exception\ConnectionException;
use Icewind\SMB\Exception\InvalidHostException;
use Icewind\SMB\Exception\NoLoginServerException;
class Connection extends RawConnection {
const DELIMITER = 'smb:';
@@ -26,18 +28,25 @@ class Connection extends RawConnection {
/**
* get all unprocessed output from smbclient until the next prompt
*
* @throws ConnectionException
* @return string
* @throws AuthenticationException
* @throws ConnectException
* @throws ConnectionException
* @throws InvalidHostException
* @throws NoLoginServerException
*/
public function read() {
if (!$this->isValid()) {
throw new ConnectionException();
throw new ConnectionException('Connection not valid');
}
$line = $this->readLine(); //first line is prompt
$this->checkConnectionError($line);
$output = array();
$line = $this->readLine();
if ($line === false) {
throw new ConnectException('Unknown error');
}
$length = mb_strlen(self::DELIMITER);
while (mb_substr($line, 0, $length) !== self::DELIMITER) { //next prompt functions as delimiter
$output[] .= $line;
@@ -52,20 +61,24 @@ class Connection extends RawConnection {
* @param $line
* @throws AuthenticationException
* @throws InvalidHostException
* @throws NoLoginServerException
*/
private function checkConnectionError($line) {
$line = rtrim($line, ')');
if (substr($line, -23) === ErrorCodes::LogonFailure) {
throw new AuthenticationException();
throw new AuthenticationException('Invalid login');
}
if (substr($line, -26) === ErrorCodes::BadHostName) {
throw new InvalidHostException();
throw new InvalidHostException('Invalid hostname');
}
if (substr($line, -22) === ErrorCodes::Unsuccessful) {
throw new InvalidHostException();
throw new InvalidHostException('Connection unsuccessful');
}
if (substr($line, -28) === ErrorCodes::ConnectionRefused) {
throw new InvalidHostException();
throw new InvalidHostException('Connection refused');
}
if (substr($line, -26) === ErrorCodes::NoLogonServers) {
throw new NoLoginServerException('No login server');
}
}
@@ -15,6 +15,7 @@ class ErrorCodes {
const BadHostName = 'NT_STATUS_BAD_NETWORK_NAME';
const Unsuccessful = 'NT_STATUS_UNSUCCESSFUL';
const ConnectionRefused = 'NT_STATUS_CONNECTION_REFUSED';
const NoLogonServers = 'NT_STATUS_NO_LOGON_SERVERS';
const PathNotFound = 'NT_STATUS_OBJECT_PATH_NOT_FOUND';
const NoSuchFile = 'NT_STATUS_NO_SUCH_FILE';
@@ -0,0 +1,10 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Exception;
class InvalidPathException extends InvalidRequestException {}
@@ -5,5 +5,6 @@
* http://opensource.org/licenses/MIT
*/
date_default_timezone_set('UTC');
require_once __DIR__.'/../vendor/autoload.php';
namespace Icewind\SMB\Exception;
class NoLoginServerException extends ConnectException {}
@@ -24,12 +24,7 @@ class NativeServer extends Server {
}
protected function connect() {
$user = $this->getUser();
$workgroup = null;
if (strpos($user, '/')) {
list($workgroup, $user) = explode($user, '/');
}
$this->state->init($workgroup, $user, $this->getPassword());
$this->state->init($this->getWorkgroup(), $this->getUser(), $this->getPassword());
}
/**
@@ -7,7 +7,7 @@
namespace Icewind\SMB;
class NativeShare implements IShare {
class NativeShare extends AbstractShare {
/**
* @var Server $server
*/
@@ -28,6 +28,7 @@ class NativeShare implements IShare {
* @param string $name
*/
public function __construct($server, $name) {
parent::__construct();
$this->server = $server;
$this->name = $name;
$this->state = new NativeState();
@@ -43,15 +44,7 @@ class NativeShare implements IShare {
return;
}
$user = $this->server->getUser();
if (strpos($user, '/')) {
list($workgroup, $user) = explode('/', $user);
} elseif (strpos($user, '\\')) {
list($workgroup, $user) = explode('\\', $user);
} else {
$workgroup = null;
}
$this->state->init($workgroup, $user, $this->server->getPassword());
$this->state->init($this->server->getWorkgroup(), $this->server->getUser(), $this->server->getPassword());
}
/**
@@ -64,6 +57,7 @@ class NativeShare implements IShare {
}
private function buildUrl($path) {
$this->verifyPath($path);
$url = sprintf('smb://%s/%s', $this->server->getHost(), $this->name);
if ($path) {
$path = trim($path, '/');
@@ -149,6 +143,7 @@ class NativeShare implements IShare {
* @throws \Icewind\SMB\Exception\InvalidTypeException
*/
public function del($path) {
$this->connect();
return $this->state->unlink($this->buildUrl($path));
}
+32 -1
View File
@@ -29,6 +29,11 @@ class Server {
*/
protected $password;
/**
* @var string $workgroup
*/
protected $workgroup;
/**
* Check if the smbclient php extension is available
*
@@ -45,10 +50,28 @@ class Server {
*/
public function __construct($host, $user, $password) {
$this->host = $host;
list($workgroup, $user) = $this->splitUser($user);
$this->user = $user;
$this->workgroup = $workgroup;
$this->password = $password;
}
/**
* Split workgroup from username
*
* @param $user
* @return string[] [$workgroup, $user]
*/
public function splitUser($user) {
if (strpos($user, '/')) {
return explode('/', $user, 2);
} elseif (strpos($user, '\\')) {
return explode('\\', $user);
} else {
return array(null, $user);
}
}
/**
* @return string
*/
@@ -77,6 +100,13 @@ class Server {
return $this->host;
}
/**
* @return string
*/
public function getWorkgroup() {
return $this->workgroup;
}
/**
* @return \Icewind\SMB\IShare[]
*
@@ -84,7 +114,8 @@ class Server {
* @throws \Icewind\SMB\Exception\InvalidHostException
*/
public function listShares() {
$command = Server::CLIENT . ' --authentication-file=/proc/self/fd/3' .
$workgroupArgument = ($this->workgroup) ? ' -W ' . escapeshellarg($this->workgroup) : '';
$command = Server::CLIENT . $workgroupArgument . ' --authentication-file=/proc/self/fd/3' .
' -gL ' . escapeshellarg($this->getHost());
$connection = new RawConnection($command);
$connection->writeAuthentication($this->getUser(), $this->getPassword());
+22 -22
View File
@@ -7,17 +7,13 @@
namespace Icewind\SMB;
use Icewind\SMB\Exception\AccessDeniedException;
use Icewind\SMB\Exception\AlreadyExistsException;
use Icewind\SMB\Exception\ConnectionException;
use Icewind\SMB\Exception\Exception;
use Icewind\SMB\Exception\FileInUseException;
use Icewind\SMB\Exception\InvalidTypeException;
use Icewind\SMB\Exception\NotEmptyException;
use Icewind\SMB\Exception\NotFoundException;
use Icewind\Streams\CallbackWrapper;
class Share implements IShare {
class Share extends AbstractShare {
/**
* @var Server $server
*/
@@ -43,6 +39,7 @@ class Share implements IShare {
* @param string $name
*/
public function __construct($server, $name) {
parent::__construct();
$this->server = $server;
$this->name = $name;
$this->parser = new Parser(new TimeZoneProvider($this->server->getHost()));
@@ -57,10 +54,11 @@ class Share implements IShare {
if ($this->connection and $this->connection->isValid()) {
return;
}
$command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s',
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : '';
$command = sprintf('%s %s --authentication-file=/proc/self/fd/3 %s',
Server::CLIENT,
$this->server->getHost(),
$this->name
$workgroupArgument,
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
);
$this->connection = new Connection($command);
$this->connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
@@ -256,18 +254,18 @@ class Share implements IShare {
*/
public function read($source) {
$source = $this->escapePath($source);
// close the single quote, open a double quote where we put the single quote...
$source = str_replace('\'', '\'"\'"\'', $source);
// since returned stream is closed by the caller we need to create a new instance
// since we can't re-use the same file descriptor over multiple calls
$command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'get %s /proc/self/fd/5\'',
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : '';
$command = sprintf('%s %s --authentication-file=/proc/self/fd/3 %s',
Server::CLIENT,
$this->server->getHost(),
$this->name,
$source
$workgroupArgument,
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
);
$connection = new Connection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$connection->write('get ' . $source . ' /proc/self/fd/5');
$connection->write('exit');
$fh = $connection->getFileOutputStream();
stream_context_set_option($fh, 'file', 'connection', $connection);
return $fh;
@@ -284,23 +282,24 @@ class Share implements IShare {
*/
public function write($target) {
$target = $this->escapePath($target);
// close the single quote, open a double quote where we put the single quote...
$target = str_replace('\'', '\'"\'"\'', $target);
// since returned stream is closed by the caller we need to create a new instance
// since we can't re-use the same file descriptor over multiple calls
$command = sprintf('%s --authentication-file=/proc/self/fd/3 //%s/%s -c \'put /proc/self/fd/4 %s\'',
$workgroupArgument = ($this->server->getWorkgroup()) ? ' -W ' . escapeshellarg($this->server->getWorkgroup()) : '';
$command = sprintf('%s %s --authentication-file=/proc/self/fd/3 %s',
Server::CLIENT,
$this->server->getHost(),
$this->name,
$target
$workgroupArgument,
escapeshellarg('//' . $this->server->getHost() . '/' . $this->name)
);
$connection = new RawConnection($command);
$connection = new Connection($command);
$connection->writeAuthentication($this->server->getUser(), $this->server->getPassword());
$fh = $connection->getFileInputStream();
$connection->write('put /proc/self/fd/4 ' . $target);
$connection->write('exit');
// use a close callback to ensure the upload is finished before continuing
// this also serves as a way to keep the connection in scope
return CallbackWrapper::wrap($fh, null, null, function () use ($connection) {
return CallbackWrapper::wrap($fh, null, null, function () use ($connection, $target) {
$connection->close(false); // dont terminate, give the upload some time
});
}
@@ -378,6 +377,7 @@ class Share implements IShare {
* @return string
*/
protected function escapePath($path) {
$this->verifyPath($path);
if ($path === '/') {
$path = '';
}
@@ -1,539 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\FileInfo;
abstract class AbstractShare extends \PHPUnit_Framework_TestCase {
/**
* @var \Icewind\SMB\Server $server
*/
protected $server;
/**
* @var \Icewind\SMB\IShare $share
*/
protected $share;
/**
* @var string $root
*/
protected $root;
protected $config;
public function tearDown() {
try {
if ($this->share) {
$this->cleanDir($this->root);
}
unset($this->share);
} catch (\Exception $e) {
unset($this->share);
throw $e;
}
}
public function nameProvider() {
// / ? < > \ : * | " are illegal characters in path on windows
return array(
array('simple'),
array('with spaces_and-underscores'),
array("single'quote'"),
array('日本語'),
array('url %2F +encode'),
array('a somewhat longer filename than the other with more charaters as the all the other filenames'),
array('$as#d€££Ö€ßœĚęĘĞĜΣΥΦΩΫ')
);
}
public function fileDataProvider() {
return array(
array('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua'),
array('Mixed language, 日本語 が わからか and Various _/* characters \\|” €')
);
}
public function nameAndDataProvider() {
$names = $this->nameProvider();
$data = $this->fileDataProvider();
$result = array();
foreach ($names as $name) {
foreach ($data as $text) {
$result[] = array($name[0], $text[0]);
}
}
return $result;
}
public function cleanDir($dir) {
$content = $this->share->dir($dir);
foreach ($content as $metadata) {
if ($metadata->isDirectory()) {
$this->cleanDir($metadata->getPath());
} else {
$this->share->del($metadata->getPath());
}
}
$this->share->rmdir($dir);
}
private function getTextFile($text = '') {
if (!$text) {
$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
}
$file = tempnam('/tmp', 'smb_test_');
file_put_contents($file, $text);
return $file;
}
public function testListShares() {
$shares = $this->server->listShares();
foreach ($shares as $share) {
if ($share->getName() === $this->config->share) {
return;
}
}
$this->fail('Share "' . $this->config->share . '" not found');
}
public function testRootStartsEmpty() {
$this->assertEquals(array(), $this->share->dir($this->root));
}
/**
* @dataProvider nameProvider
*/
public function testMkdir($name) {
$this->share->mkdir($this->root . '/' . $name);
$dirs = $this->share->dir($this->root);
$this->assertCount(1, $dirs);
$this->assertEquals($name, $dirs[0]->getName());
$this->assertTrue($dirs[0]->isDirectory());
}
/**
* @dataProvider nameProvider
*/
public function testRenameDirectory($name) {
$this->share->mkdir($this->root . '/' . $name);
$this->share->rename($this->root . '/' . $name, $this->root . '/' . $name . '_rename');
$dirs = $this->share->dir($this->root);
$this->assertEquals(1, count($dirs));
$this->assertEquals($name . '_rename', $dirs[0]->getName());
}
/**
* @dataProvider nameProvider
*/
public function testRmdir($name) {
$this->share->mkdir($this->root . '/' . $name);
$this->share->rmdir($this->root . '/' . $name);
$this->assertCount(0, $this->share->dir($this->root));
}
/**
* @dataProvider nameAndDataProvider
*/
public function testPut($name, $text) {
$tmpFile = $this->getTextFile($text);
$size = filesize($tmpFile);
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$files = $this->share->dir($this->root);
$this->assertCount(1, $files);
$this->assertEquals($name, $files[0]->getName());
$this->assertEquals($size, $files[0]->getSize());
$this->assertFalse($files[0]->isDirectory());
}
/**
* @dataProvider nameProvider
*/
public function testRenameFile($name) {
$tmpFile = $this->getTextFile();
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$this->share->rename($this->root . '/' . $name, $this->root . '/' . $name . '_renamed');
$files = $this->share->dir($this->root);
$this->assertEquals(1, count($files));
$this->assertEquals($name . '_renamed', $files[0]->getName());
}
/**
* @dataProvider nameAndDataProvider
*/
public function testGet($name, $text) {
$tmpFile = $this->getTextFile($text);
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$targetFile = tempnam('/tmp', 'smb_test_');
$this->share->get($this->root . '/' . $name, $targetFile);
$this->assertEquals($text, file_get_contents($targetFile));
unlink($targetFile);
}
/**
* @dataProvider nameProvider
*/
public function testDel($name) {
$tmpFile = $this->getTextFile();
$this->share->put($tmpFile, $this->root . '/' . $name);
unlink($tmpFile);
$this->share->del($this->root . '/' . $name);
$this->assertCount(0, $this->share->dir($this->root));
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testCreateFolderInNonExistingFolder() {
$this->share->mkdir($this->root . '/foo/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRemoveFolderInNonExistingFolder() {
$this->share->rmdir($this->root . '/foo/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRemoveNonExistingFolder() {
$this->share->rmdir($this->root . '/foo');
}
/**
* @expectedException \Icewind\SMB\Exception\AlreadyExistsException
*/
public function testCreateExistingFolder() {
$this->share->mkdir($this->root . '/bar');
$this->share->mkdir($this->root . '/bar');
$this->share->rmdir($this->root . '/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testCreateFileExistingFolder() {
$this->share->mkdir($this->root . '/bar');
$this->share->put($this->getTextFile(), $this->root . '/bar');
$this->share->rmdir($this->root . '/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testCreateFileInNonExistingFolder() {
$this->share->put($this->getTextFile(), $this->root . '/foo/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testTestRemoveNonExistingFile() {
$this->share->del($this->root . '/foo');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testDownloadNonExistingFile() {
$this->share->get($this->root . '/foo', '/dev/null');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testDownloadFolder() {
$this->share->mkdir($this->root . '/foobar');
$this->share->get($this->root . '/foobar', '/dev/null');
$this->share->rmdir($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testDelFolder() {
$this->share->mkdir($this->root . '/foobar');
$this->share->del($this->root . '/foobar');
$this->share->rmdir($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidTypeException
*/
public function testRmdirFile() {
$this->share->put($this->getTextFile(), $this->root . '/foobar');
$this->share->rmdir($this->root . '/foobar');
$this->share->del($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotEmptyException
*/
public function testRmdirNotEmpty() {
$this->share->mkdir($this->root . '/foobar');
$this->share->put($this->getTextFile(), $this->root . '/foobar/asd');
$this->share->rmdir($this->root . '/foobar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testDirNonExisting() {
$this->share->dir('/foobar/asd');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRmDirNonExisting() {
$this->share->rmdir('/foobar/asd');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRenameNonExisting() {
$this->share->rename('/foobar/asd', '/foobar/bar');
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testRenameTargetNonExisting() {
$txt = $this->getTextFile();
$this->share->put($txt, $this->root . '/foo.txt');
unlink($txt);
$this->share->rename($this->root . '/foo.txt', $this->root . '/bar/foo.txt');
}
public function testModifiedDate() {
$now = time();
$this->share->put($this->getTextFile(), $this->root . '/foo.txt');
$dir = $this->share->dir($this->root);
$mtime = $dir[0]->getMTime();
$this->assertTrue(abs($now - $mtime) <= 2, 'Modified time differs by ' . abs($now - $mtime) . ' seconds');
$this->share->del($this->root . '/foo.txt');
}
/**
* @dataProvider nameAndDataProvider
*/
public function testReadStream($name, $text) {
$sourceFile = $this->getTextFile($text);
$this->share->put($sourceFile, $this->root . '/' . $name);
$fh = $this->share->read($this->root . '/' . $name);
$content = stream_get_contents($fh);
fclose($fh);
$this->share->del($this->root . '/' . $name);
$this->assertEquals(file_get_contents($sourceFile), $content);
}
/**
* @dataProvider nameAndDataProvider
*/
public function testWriteStream($name, $text) {
$fh = $this->share->write($this->root . '/' . $name);
fwrite($fh, $text);
fclose($fh);
$tmpFile1 = tempnam('/tmp', 'smb_test_');
$this->share->get($this->root . '/' . $name, $tmpFile1);
$this->assertEquals($text, file_get_contents($tmpFile1));
$this->share->del($this->root . '/' . $name);
unlink($tmpFile1);
}
public function testDir() {
$txtFile = $this->getTextFile();
$this->share->mkdir($this->root . '/dir');
$this->share->put($txtFile, $this->root . '/file.txt');
unlink($txtFile);
$dir = $this->share->dir($this->root);
if ($dir[0]->getName() === 'dir') {
$dirEntry = $dir[0];
} else {
$dirEntry = $dir[1];
}
$this->assertTrue($dirEntry->isDirectory());
$this->assertFalse($dirEntry->isReadOnly());
$this->assertFalse($dirEntry->isReadOnly());
if ($dir[0]->getName() === 'file.txt') {
$fileEntry = $dir[0];
} else {
$fileEntry = $dir[1];
}
$this->assertFalse($fileEntry->isDirectory());
$this->assertFalse($fileEntry->isReadOnly());
$this->assertFalse($fileEntry->isReadOnly());
}
/**
* @dataProvider nameProvider
*/
public function testStat($name) {
$txtFile = $this->getTextFile();
$size = filesize($txtFile);
$this->share->put($txtFile, $this->root . '/' . $name);
unlink($txtFile);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertEquals($size, $info->getSize());
}
/**
* @expectedException \Icewind\SMB\Exception\NotFoundException
*/
public function testStatNonExisting() {
$this->share->stat($this->root . '/fo.txt');
}
/**
* note setting archive and system bit is not supported
*
* @dataProvider nameProvider
*/
public function testSetMode($name) {
$txtFile = $this->getTextFile();
$this->share->put($txtFile, $this->root . '/' . $name);
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_NORMAL);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_READONLY);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertTrue($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_ARCHIVE);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertTrue($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertTrue($info->isReadOnly());
$this->assertTrue($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_HIDDEN);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertTrue($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_SYSTEM);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertTrue($info->isSystem());
$this->assertFalse($info->isHidden());
$this->share->setMode($this->root . '/' . $name, FileInfo::MODE_NORMAL);
$info = $this->share->stat($this->root . '/' . $name);
$this->assertFalse($info->isReadOnly());
$this->assertFalse($info->isArchived());
$this->assertFalse($info->isSystem());
$this->assertFalse($info->isHidden());
}
public function pathProvider() {
// / ? < > \ : * | " are illegal characters in path on windows
return array(
array('dir/sub/foo.txt'),
array('bar.txt'),
array("single'quote'/sub/foo.txt"),
array('日本語/url %2F +encode/asd.txt'),
array(
'a somewhat longer folder than the other with more charaters as the all the other filenames/' .
'followed by a somewhat long file name after that.txt'
)
);
}
/**
* @dataProvider pathProvider
*/
public function testSubDirs($path) {
$dirs = explode('/', $path);
$name = array_pop($dirs);
$fullPath = '';
foreach ($dirs as $dir) {
$fullPath .= '/' . $dir;
$this->share->mkdir($this->root . $fullPath);
}
$txtFile = $this->getTextFile();
$size = filesize($txtFile);
$this->share->put($txtFile, $this->root . $fullPath . '/' . $name);
unlink($txtFile);
$info = $this->share->stat($this->root . $fullPath . '/' . $name);
$this->assertEquals($size, $info->getSize());
$this->assertFalse($info->isHidden());
}
public function testDelAfterStat() {
$name = 'foo.txt';
$txtFile = $this->getTextFile();
$this->share->put($txtFile, $this->root . '/' . $name);
unlink($txtFile);
$this->share->stat($this->root . '/' . $name);
$this->share->del($this->root . '/foo.txt');
}
/**
* @param $name
* @dataProvider nameProvider
*/
public function testDirPaths($name) {
$txtFile = $this->getTextFile();
$this->share->mkdir($this->root . '/' . $name);
$this->share->put($txtFile, $this->root . '/' . $name . '/' . $name);
unlink($txtFile);
$content = $this->share->dir($this->root . '/' . $name);
$this->assertCount(1, $content);
$this->assertEquals($name, $content[0]->getName());
}
public function testStatRoot() {
$info = $this->share->stat('/');
$this->assertInstanceOf('\Icewind\SMB\IFileInfo', $info);
}
}
@@ -1,27 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\NativeServer;
class NativeShare extends AbstractShare {
public function setUp() {
if (!function_exists('smbclient_state_new')) {
$this->markTestSkipped('libsmbclient php extension not installed');
}
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new NativeServer($this->config->host, $this->config->user, $this->config->password);
$this->share = $this->server->getShare($this->config->share);
if ($this->config->root) {
$this->root = '/' . $this->config->root . '/' . uniqid();
} else {
$this->root = '/' . uniqid();
}
$this->share->mkdir($this->root);
}
}
@@ -1,143 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\NativeServer;
class NativeStream extends \PHPUnit_Framework_TestCase {
/**
* @var \Icewind\SMB\Server $server
*/
protected $server;
/**
* @var \Icewind\SMB\NativeShare $share
*/
protected $share;
/**
* @var string $root
*/
protected $root;
protected $config;
public function setUp() {
if (!function_exists('smbclient_state_new')) {
$this->markTestSkipped('libsmbclient php extension not installed');
}
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new NativeServer($this->config->host, $this->config->user, $this->config->password);
$this->share = $this->server->getShare($this->config->share);
if ($this->config->root) {
$this->root = '/' . $this->config->root . '/' . uniqid();
} else {
$this->root = '/' . uniqid();
}
$this->share->mkdir($this->root);
}
private function getTextFile() {
$text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
$file = tempnam('/tmp', 'smb_test_');
file_put_contents($file, $text);
return $file;
}
public function testSeekTell() {
$sourceFile = $this->getTextFile();
$this->share->put($sourceFile, $this->root . '/foobar');
$fh = $this->share->read($this->root . '/foobar');
$content = fread($fh, 3);
$this->assertEquals('Lor', $content);
fseek($fh, -2, SEEK_CUR);
$content = fread($fh, 3);
$this->assertEquals('ore', $content);
fseek($fh, 3, SEEK_SET);
$content = fread($fh, 3);
$this->assertEquals('em ', $content);
fseek($fh, -3, SEEK_END);
$content = fread($fh, 3);
$this->assertEquals('qua', $content);
fseek($fh, -3, SEEK_END);
$this->assertEquals(120, ftell($fh));
}
public function testStat() {
$sourceFile = $this->getTextFile();
$this->share->put($sourceFile, $this->root . '/foobar');
$fh = $this->share->read($this->root . '/foobar');
$stat = fstat($fh);
$this->assertEquals(filesize($sourceFile), $stat['size']);
unlink($sourceFile);
}
public function testTruncate() {
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers');
}
$fh = $this->share->write($this->root . '/foobar');
fwrite($fh, 'foobar');
ftruncate($fh, 3);
fclose($fh);
$fh = $this->share->read($this->root . '/foobar');
$this->assertEquals('foo', stream_get_contents($fh));
}
public function testEOF() {
if (version_compare(phpversion(), '5.4.0', '<')) {
$this->markTestSkipped('php <5.4 doesn\'t support truncate for stream wrappers');
}
$fh = $this->share->write($this->root . '/foobar');
fwrite($fh, 'foobar');
fclose($fh);
$fh = $this->share->read($this->root . '/foobar');
fread($fh, 3);
$this->assertFalse(feof($fh));
fread($fh, 5);
$this->assertTrue(feof($fh));
}
public function testLockUnsupported() {
$fh = $this->share->write($this->root . '/foobar');
$this->assertFalse(flock($fh, LOCK_SH));
}
public function testSetOptionUnsupported() {
$fh = $this->share->write($this->root . '/foobar');
$this->assertFalse(stream_set_blocking($fh, false));
}
public function tearDown() {
if ($this->share) {
$this->cleanDir($this->root);
}
unset($this->share);
}
public function cleanDir($dir) {
$content = $this->share->dir($dir);
foreach ($content as $metadata) {
if ($metadata->isDirectory()) {
$this->cleanDir($metadata->getPath());
} else {
$this->share->del($metadata->getPath());
}
}
$this->share->rmdir($dir);
}
}
@@ -1,103 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\FileInfo;
class Parser extends \PHPUnit_Framework_TestCase {
public function modeProvider() {
return array(
array('D', FileInfo::MODE_DIRECTORY),
array('A', FileInfo::MODE_ARCHIVE),
array('S', FileInfo::MODE_SYSTEM),
array('H', FileInfo::MODE_HIDDEN),
array('R', FileInfo::MODE_READONLY),
array('N', FileInfo::MODE_NORMAL),
array('RA', FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE),
array('RAH', FileInfo::MODE_READONLY | FileInfo::MODE_ARCHIVE | FileInfo::MODE_HIDDEN)
);
}
/**
* @param string $timeZone
* @return \Icewind\SMB\TimeZoneProvider
*/
private function getTimeZoneProvider($timeZone) {
$mock = $this->getMockBuilder('\Icewind\SMB\TimeZoneProvider')
->disableOriginalConstructor()
->getMock();
$mock->expects($this->any())
->method('get')
->will($this->returnValue($timeZone));
return $mock;
}
/**
* @dataProvider modeProvider
*/
public function testParseMode($string, $mode) {
$parser = new \Icewind\SMB\Parser($this->getTimeZoneProvider('UTC'));
$this->assertEquals($mode, $parser->parseMode($string), 'Failed parsing ' . $string);
}
public function statProvider() {
return array(
array(
array(
'altname: test.txt',
'create_time: Sat Oct 12 07:05:58 PM 2013 CEST',
'access_time: Tue Oct 15 02:58:48 PM 2013 CEST',
'write_time: Sat Oct 12 07:05:58 PM 2013 CEST',
'change_time: Sat Oct 12 07:05:58 PM 2013 CEST',
'attributes: (80)',
'stream: [::$DATA], 29634 bytes'
),
array(
'mtime' => strtotime('12 Oct 2013 19:05:58 CEST'),
'mode' => FileInfo::MODE_NORMAL,
'size' => 29634
)
)
);
}
/**
* @dataProvider statProvider
*/
public function testStat($output, $stat) {
$parser = new \Icewind\SMB\Parser($this->getTimeZoneProvider('UTC'));
$this->assertEquals($stat, $parser->parseStat($output));
}
public function dirProvider() {
return array(
array(
array(
' . D 0 Tue Aug 26 19:11:56 2014',
' .. DR 0 Sun Oct 28 15:24:02 2012',
' c.pdf N 29634 Sat Oct 12 19:05:58 2013',
'',
' 62536 blocks of size 8388608. 57113 blocks available'
),
array(
new FileInfo('/c.pdf', 'c.pdf', 29634, strtotime('12 Oct 2013 19:05:58 CEST'),
FileInfo::MODE_NORMAL)
)
)
);
}
/**
* @dataProvider dirProvider
*/
public function testDir($output, $dir) {
$parser = new \Icewind\SMB\Parser($this->getTimeZoneProvider('CEST'));
$this->assertEquals($dir, $parser->parseDir($output, ''));
}
}
@@ -1,57 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
class Server extends \PHPUnit_Framework_TestCase {
/**
* @var \Icewind\SMB\Server $server
*/
private $server;
private $config;
public function setUp() {
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new \Icewind\SMB\Server($this->config->host, $this->config->user, $this->config->password);
}
public function testListShares() {
$shares = $this->server->listShares();
foreach ($shares as $share) {
if ($share->getName() === $this->config->share) {
return;
}
}
$this->fail('Share "' . $this->config->share . '" not found');
}
/**
* @expectedException \Icewind\SMB\Exception\AuthenticationException
*/
public function testWrongUserName() {
$this->markTestSkipped('This fails for no reason on travis');
$server = new \Icewind\SMB\Server($this->config->host, uniqid(), uniqid());
$server->listShares();
}
/**
* @expectedException \Icewind\SMB\Exception\AuthenticationException
*/
public function testWrongPassword() {
$server = new \Icewind\SMB\Server($this->config->host, $this->config->user, uniqid());
$server->listShares();
}
/**
* @expectedException \Icewind\SMB\Exception\InvalidHostException
*/
public function testWrongHost() {
$server = new \Icewind\SMB\Server(uniqid(), $this->config->user, $this->config->password);
$server->listShares();
}
}
@@ -1,24 +0,0 @@
<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Test;
use Icewind\SMB\Server as NormalServer;
class Share extends AbstractShare {
public function setUp() {
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
$this->server = new NormalServer($this->config->host, $this->config->user, $this->config->password);
$this->share = $this->server->getShare($this->config->share);
if ($this->config->root) {
$this->root = '/' . $this->config->root . '/' . uniqid();
} else {
$this->root = '/' . uniqid();
}
$this->share->mkdir($this->root);
}
}
@@ -1,7 +0,0 @@
{
"host": "localhost",
"user": "test",
"password": "test",
"share": "test",
"root": "test"
}
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<phpunit bootstrap="bootstrap.php">
<testsuite name='SMB'>
<directory suffix='.php'>./</directory>
</testsuite>
</phpunit>
@@ -90,6 +90,15 @@ abstract class StoragesController extends Controller {
}
// TODO: validate that other attrs are set
if ($storage->getBackendOption('objectstore')) {
// objectstore must not be sent from client side
return new DataResponse(
array(
'message' => (string)$this->l10n->t('Objectstore forbidden')
),
Http::STATUS_UNPROCESSABLE_ENTITY
);
}
$backends = \OC_Mount_Config::getBackends();
if (!isset($backends[$storage->getBackendClass()])) {
+2 -2
View File
@@ -121,7 +121,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
$params['region'] = empty($params['region']) ? 'eu-west-1' : $params['region'];
$params['hostname'] = empty($params['hostname']) ? 's3.amazonaws.com' : $params['hostname'];
if (!isset($params['port']) || $params['port'] === '') {
$params['port'] = ($params['use_ssl'] === 'false') ? 80 : 443;
$params['port'] = ($params['use_ssl'] === false || $params['use_ssl'] === 'false') ? 80 : 443;
}
$this->params = $params;
}
@@ -586,7 +586,7 @@ class AmazonS3 extends \OC\Files\Storage\Common {
return $this->connection;
}
$scheme = ($this->params['use_ssl'] === 'false') ? 'http' : 'https';
$scheme = ($this->params['use_ssl'] === false || $this->params['use_ssl'] === 'false') ? 'http' : 'https';
$base_url = $scheme . '://' . $this->params['hostname'] . ':' . $this->params['port'] . '/';
$this->connection = S3Client::factory(array(
+3 -1
View File
@@ -297,7 +297,9 @@ class OC_Mount_Config {
}
}
} else {
$input = str_replace('$user', $user, $input);
if (is_string($input)) {
$input = str_replace('$user', $user, $input);
}
}
return $input;
}
+4 -1
View File
@@ -78,7 +78,10 @@ class SMB extends Common {
* @return string
*/
public function getId() {
return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '/' . $this->share->getName() . '/' . $this->root;
// FIXME: double slash to keep compatible with the old storage ids,
// failure to do so will lead to creation of a new storage id and
// loss of shares from the storage
return 'smb::' . $this->server->getUser() . '@' . $this->server->getHost() . '//' . $this->share->getName() . '/' . $this->root;
}
/**
+1 -1
View File
@@ -39,7 +39,7 @@ class SMB_OC extends SMB {
public function __construct($params) {
if (isset($params['host'])) {
$host = $params['host'];
$this->username_as_share = ($params['username_as_share'] === 'true');
$this->username_as_share = ($params['username_as_share'] === true);
// dummy credentials, unused, to satisfy constructor
$user = 'foo';
+19
View File
@@ -173,6 +173,25 @@ class StorageConfig implements \JsonSerializable {
$this->backendOptions = $backendOptions;
}
/**
* @param string $option
* @return mixed
*/
public function getBackendOption($key) {
if (isset($this->backendOptions[$key])) {
return $this->backendOptions[$key];
}
return null;
}
/**
* @param string $option
* @param mixed $value
*/
public function setBackendOption($key, $value) {
$this->backendOptions[$key] = $value;
}
/**
* Returns the mount priority
*
+4 -1
View File
@@ -40,8 +40,11 @@ abstract class StreamWrapper extends Common {
}
public function rmdir($path) {
if ($this->file_exists($path) && $this->isDeletable($path)) {
if ($this->is_dir($path) && $this->isDeletable($path)) {
$dh = $this->opendir($path);
if (!is_resource($dh)) {
return false;
}
while (($file = readdir($dh)) !== false) {
if ($this->is_dir($path . '/' . $file)) {
$this->rmdir($path . '/' . $file);
@@ -352,10 +352,14 @@ abstract class StoragesService {
if (!isset($allStorages[$id])) {
throw new NotFoundException('Storage with id "' . $id . '" not found');
}
$oldStorage = $allStorages[$id];
$allStorages[$id] = $updatedStorage;
// ensure objectstore is persistent
if ($objectstore = $oldStorage->getBackendOption('objectstore')) {
$updatedStorage->setBackendOption('objectstore', $objectstore);
}
$allStorages[$id] = $updatedStorage;
$this->writeConfig($allStorages);
$this->triggerChangeHooks($oldStorage, $updatedStorage);
+1 -1
View File
@@ -60,7 +60,7 @@
<?php elseif (strpos($placeholder, '!') === 0): ?>
<label><input type="checkbox"
data-parameter="<?php p($parameter); ?>"
<?php if ($value == 'true'): ?> checked="checked"<?php endif; ?>
<?php if ($value === true || $value === 'true'): ?> checked="checked"<?php endif; ?>
/><?php p(substr($placeholder, 1)); ?></label>
<?php elseif (strpos($placeholder, '#') === 0): ?>
<input type="hidden"
@@ -61,4 +61,16 @@ class SMB extends Storage {
$this->assertTrue($result);
$this->assertTrue($this->instance->is_dir('foo bar'));
}
public function testStorageId() {
$this->instance = new \OC\Files\Storage\SMB([
'host' => 'testhost',
'user' => 'testuser',
'password' => 'somepass',
'share' => 'someshare',
'root' => 'someroot',
]);
$this->assertEquals('smb::testuser@testhost//someshare//someroot/', $this->instance->getId());
$this->instance = null;
}
}
+7 -1
View File
@@ -170,8 +170,14 @@ class Server2Server {
$query = \OCP\DB::prepare('DELETE FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?');
$query->execute(array($id, $token));
if ($share['accepted']) {
$path = trim($mountpoint, '/');
} else {
$path = trim($share['name'], '/');
}
\OC::$server->getActivityManager()->publishActivity(
'files_sharing', \OCA\Files_Sharing\Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $mountpoint), '', array(),
'files_sharing', \OCA\Files_Sharing\Activity::SUBJECT_REMOTE_SHARE_UNSHARED, array($owner, $path), '', array(),
'', '', $user, \OCA\Files_Sharing\Activity::TYPE_REMOTE_SHARE, \OCA\Files_Sharing\Activity::PRIORITY_MEDIUM);
}
-1
View File
@@ -19,5 +19,4 @@ Turning the feature off removes shared files and folders on the server for all s
<files>public.php</files>
<webdav>publicwebdav.php</webdav>
</public>
<ocsid>166050</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.6.1
0.6.2
+1 -1
View File
@@ -72,7 +72,7 @@ thead {
}
/* keep long file names in one line to not overflow download button on mobile */
.directDownload #download {
.directDownload #downloadFile {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
+13 -11
View File
@@ -158,9 +158,18 @@ OCA.Sharing.PublicApp = {
};
this.fileList.generatePreviewUrl = function (urlSpec) {
urlSpec = urlSpec || {};
if (!urlSpec.x) {
urlSpec.x = 36;
}
if (!urlSpec.y) {
urlSpec.y = 36;
}
urlSpec.x *= window.devicePixelRatio;
urlSpec.y *= window.devicePixelRatio;
urlSpec.x = Math.floor(urlSpec.x);
urlSpec.y = Math.floor(urlSpec.y);
urlSpec.t = $('#dirToken').val();
urlSpec.y = Math.floor(36 * window.devicePixelRatio);
urlSpec.x = Math.floor(36 * window.devicePixelRatio);
return OC.generateUrl('/apps/files_sharing/ajax/publicpreview.php?') + $.param(urlSpec);
};
@@ -292,15 +301,8 @@ $(document).ready(function () {
if (window.Files) {
// HACK: for oc-dialogs previews that depends on Files:
Files.lazyLoadPreview = function (path, mime, ready, width, height, etag) {
return App.fileList.lazyLoadPreview({
path: path,
mime: mime,
callback: ready,
width: width,
height: height,
etag: etag
});
Files.generatePreviewUrl = function (urlSpec) {
return App.fileList.generatePreviewUrl(urlSpec);
};
}
});
+1 -1
View File
@@ -151,7 +151,7 @@
var permissions = $tr.data('permissions');
var hasLink = !!(shareStatus && shareStatus.link);
OC.Share.markFileAsShared($tr, true, hasLink);
if ((permissions & OC.PERMISSION_SHARE) === 0) {
if ((permissions & OC.PERMISSION_SHARE) === 0 && $tr.attr('data-share-owner')) {
// if no share action exists because the admin disabled sharing for this user
// we create a share notification action to inform the user about files
// shared with him otherwise we just update the existing share action.
+1 -1
View File
@@ -190,12 +190,12 @@ class Activity implements IExtension {
if ($app === self::FILES_SHARING_APP) {
switch ($text) {
case self::SUBJECT_REMOTE_SHARE_RECEIVED:
case self::SUBJECT_REMOTE_SHARE_UNSHARED:
return array(
0 => '',// We can not use 'username' since the user is in a different ownCloud
);
case self::SUBJECT_REMOTE_SHARE_ACCEPTED:
case self::SUBJECT_REMOTE_SHARE_DECLINED:
case self::SUBJECT_REMOTE_SHARE_UNSHARED:
return array(
0 => '',// We can not use 'username' since the user is in a different ownCloud
1 => 'file',
+2 -2
View File
@@ -60,7 +60,7 @@ class Shared_Cache extends Cache {
if ($target === false || $target === $this->storage->getMountPoint()) {
$target = '';
}
$source = \OC_Share_Backend_File::getSource($target, $this->storage->getMountPoint(), $this->storage->getItemType());
$source = \OC_Share_Backend_File::getSource($target, $this->storage->getShare());
if (isset($source['path']) && isset($source['fileOwner'])) {
\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
$mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']);
@@ -242,7 +242,7 @@ class Shared_Cache extends Cache {
*/
protected function getMoveInfo($path) {
$cache = $this->getSourceCache($path);
$file = \OC_Share_Backend_File::getSource($path, $this->storage->getMountPoint(), $this->storage->getItemType());
$file = \OC_Share_Backend_File::getSource($path, $this->storage->getShare());
return [$cache->getNumericStorageId(), $file['path']];
}
+5 -5
View File
@@ -59,20 +59,20 @@ class Capabilities {
$public['enabled'] = $this->config->getAppValue('core', 'shareapi_allow_links', 'yes') === 'yes';
if ($public['enabled']) {
$public['password'] = [];
$public['password']['enforced'] = ($this->config->getAppValue('core', 'shareapi_enforce_links_password', 'yes') === 'yes');
$public['password']['enforced'] = ($this->config->getAppValue('core', 'shareapi_enforce_links_password', 'no') === 'yes');
$public['expire_date'] = [];
$public['expire_date']['enabled'] = $this->config->getAppValue('core', 'shareapi_default_expire_date', 'yes') === 'yes';
$public['expire_date']['enabled'] = $this->config->getAppValue('core', 'shareapi_default_expire_date', 'no') === 'yes';
if ($public['expire_date']['enabled']) {
$public['expire_date']['days'] = $this->config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
$public['expire_date']['enforced'] = $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'yes') === 'yes';
$public['expire_date']['enforced'] = $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
}
$public['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'yes') === 'yes';
$public['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_public_notification', 'no') === 'yes';
}
$res["public"] = $public;
$res['user']['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'yes') === 'yes';
$res['user']['send_mail'] = $this->config->getAppValue('core', 'shareapi_allow_mail_notification', 'no') === 'yes';
$res['resharing'] = $this->config->getAppValue('core', 'shareapi_allow_resharing', 'yes') === 'yes';
+2 -1
View File
@@ -48,9 +48,10 @@ class Scanner extends \OC\Files\Cache\Scanner {
* @param int $reuseExisting
* @param int $parentId
* @param array | null $cacheData existing data in the cache for the file to be scanned
* @param bool $lock set to false to disable getting an additional read lock during scanning
* @return array an array of metadata of the scanned file
*/
public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null) {
public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true) {
try {
return parent::scanFile($file, $reuseExisting);
} catch (ForbiddenException $e) {
+7 -13
View File
@@ -191,7 +191,7 @@ class Storage extends DAV implements ISharedStorage {
throw new StorageInvalidException();
} else {
// ownCloud instance is gone, likely to be a temporary server configuration error
throw $e;
throw new StorageNotAvailableException();
}
} catch (ForbiddenException $e) {
// auth error, remove share for now (provide a dialog in the future)
@@ -201,10 +201,7 @@ class Storage extends DAV implements ISharedStorage {
} catch (\GuzzleHttp\Exception\ConnectException $e) {
throw new StorageNotAvailableException();
} catch (\GuzzleHttp\Exception\RequestException $e) {
if ($e->getCode() === 503) {
throw new StorageNotAvailableException();
}
throw $e;
throw new StorageNotAvailableException();
} catch (\Exception $e) {
throw $e;
}
@@ -250,16 +247,13 @@ class Storage extends DAV implements ISharedStorage {
try {
$response = $client->post($url, ['body' => ['password' => $password]]);
} catch (\GuzzleHttp\Exception\RequestException $e) {
switch ($e->getCode()) {
case 401:
case 403:
if ($e->getCode() === 401 || $e->getCode() === 403) {
throw new ForbiddenException();
case 404:
throw new NotFoundException();
case 500:
throw new \Exception();
}
throw $e;
// throw this to be on the safe side: the share will still be visible
// in the UI in case the failure is intermittent, and the user will
// be able to decide whether to remove it if it's really gone
throw new StorageNotAvailableException();
}
return json_decode($response->getBody(), true);
@@ -133,8 +133,8 @@ class RecipientPropagator {
$this->markDirty($share, microtime(true));
// propagate up the share tree
$user = $share['uid_owner'];
if($user !== $this->userId) {
if ($share['share_with'] === $this->userId) {
$user = $share['uid_owner'];
$view = new View('/' . $user . '/files');
$path = $view->getPath($share['file_source']);
$watcher = new ChangeWatcher($view, $this->manager->getSharePropagator($user));
+7 -19
View File
@@ -206,27 +206,15 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent {
/**
* @param string $target
* @param string $mountPoint
* @param string $itemType
* @param array $share
* @return array|false source item
*/
public static function getSource($target, $mountPoint, $itemType) {
if ($itemType === 'folder') {
$source = \OCP\Share::getItemSharedWith('folder', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE);
if ($source && $target !== '') {
// note: in case of ext storage mount points the path might be empty
// which would cause a leading slash to appear
$source['path'] = ltrim($source['path'] . '/' . $target, '/');
}
} else {
$source = \OCP\Share::getItemSharedWith('file', $mountPoint, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE);
public static function getSource($target, $share) {
if ($share['item_type'] === 'folder' && $target !== '') {
// note: in case of ext storage mount points the path might be empty
// which would cause a leading slash to appear
$share['path'] = ltrim($share['path'] . '/' . $target, '/');
}
if ($source) {
return self::resolveReshares($source);
}
\OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::DEBUG);
return false;
return self::resolveReshares($share);
}
}
+17 -10
View File
@@ -41,34 +41,40 @@ class SharedMount extends MountPoint implements MoveableMount {
*/
protected $ownerPropagator;
/**
* @var \OC\Files\View
*/
private $recipientView;
public function __construct($storage, $mountpoint, $arguments = null, $loader = null) {
// first update the mount point before creating the parent
$this->ownerPropagator = $arguments['propagator'];
$newMountPoint = $this->verifyMountPoint($arguments['share'], $arguments['user']);
$this->recipientView = new View('/' . $arguments['user'] . '/files');
$newMountPoint = $this->verifyMountPoint($arguments['share']);
$absMountPoint = '/' . $arguments['user'] . '/files' . $newMountPoint;
$arguments['ownerView'] = new View('/' . $arguments['share']['uid_owner'] . '/files');
parent::__construct($storage, $absMountPoint, $arguments, $loader);
}
/**
* check if the parent folder exists otherwise move the mount point up
*/
private function verifyMountPoint(&$share, $user) {
private function verifyMountPoint(&$share) {
$mountPoint = basename($share['file_target']);
$parent = dirname($share['file_target']);
$view = new View('/' . $user . '/files');
if (!$view->is_dir($parent)) {
if (!$this->recipientView->is_dir($parent)) {
$parent = Helper::getShareFolder();
}
$newMountPoint = \OCA\Files_Sharing\Helper::generateUniqueTarget(
\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
array(),
new \OC\Files\View('/' . $user . '/files')
);
\OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint),
[],
$this->recipientView
);
if($newMountPoint !== $share['file_target']) {
if ($newMountPoint !== $share['file_target']) {
self::updateFileTarget($newMountPoint, $share);
$share['file_target'] = $newMountPoint;
$share['unique_name'] = true;
@@ -79,6 +85,7 @@ class SharedMount extends MountPoint implements MoveableMount {
/**
* update fileTarget in the database if the mount point changed
*
* @param string $newPath
* @param array $share reference to the share which should be modified
* @return bool
@@ -99,7 +106,7 @@ class SharedMount extends MountPoint implements MoveableMount {
'Update `*PREFIX*share`
SET `file_target` = ?
WHERE `id` = ?'
);
);
$arguments = array($newPath, $share['id']);
}
+18 -2
View File
@@ -45,8 +45,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
private $files = array();
private static $isInitialized = array();
/**
* @var \OC\Files\View
*/
private $ownerView;
public function __construct($arguments) {
$this->share = $arguments['share'];
$this->ownerView = $arguments['ownerView'];
}
/**
@@ -77,14 +83,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
if (!isset($this->files[$target])) {
// Check for partial files
if (pathinfo($target, PATHINFO_EXTENSION) === 'part') {
$source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getMountPoint(), $this->getItemType());
$source = \OC_Share_Backend_File::getSource(substr($target, 0, -5), $this->getShare());
if ($source) {
$source['path'] .= '.part';
// All partial files have delete permission
$source['permissions'] |= \OCP\Constants::PERMISSION_DELETE;
}
} else {
$source = \OC_Share_Backend_File::getSource($target, $this->getMountPoint(), $this->getItemType());
$source = \OC_Share_Backend_File::getSource($target, $this->getShare());
}
$this->files[$target] = $source;
}
@@ -623,6 +629,11 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
/** @var \OCP\Files\Storage $targetStorage */
list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
$targetStorage->acquireLock($targetInternalPath, $type, $provider);
// lock the parent folders of the owner when locking the share as recipient
if ($path === '') {
$sourcePath = $this->ownerView->getPath($this->share['file_source']);
$this->ownerView->lockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
}
}
/**
@@ -634,6 +645,11 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
/** @var \OCP\Files\Storage $targetStorage */
list($targetStorage, $targetInternalPath) = $this->resolvePath($path);
$targetStorage->releaseLock($targetInternalPath, $type, $provider);
// unlock the parent folders of the owner when unlocking the share as recipient
if ($path === '') {
$sourcePath = $this->ownerView->getPath($this->share['file_source']);
$this->ownerView->unlockFile(dirname($sourcePath), ILockingProvider::LOCK_SHARED, true);
}
}
/**
+7
View File
@@ -25,6 +25,12 @@
$l = \OC::$server->getL10N('files_sharing');
$isIE8 = false;
preg_match('/MSIE (.*?);/', $_SERVER['HTTP_USER_AGENT'], $matches);
if (count($matches) > 0 && $matches[1] <= 9) {
$isIE8 = true;
}
$uid = \OC::$server->getUserSession()->getUser()->getUID();
$server = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
$cloudID = $uid . '@' . rtrim(\OCA\Files_Sharing\Helper::removeProtocolFromUrl($server), '/');
@@ -38,5 +44,6 @@ $tmpl->assign('message_without_URL', $l->t('Share with me through my #ownCloud F
$tmpl->assign('owncloud_logo_path', $ownCloudLogoPath);
$tmpl->assign('reference', $url);
$tmpl->assign('cloudId', $cloudID);
$tmpl->assign('showShareIT', !$isIE8);
return $tmpl->fetchPage();
+1 -1
View File
@@ -100,7 +100,7 @@ $thumbSize = 1024;
<div id="imgframe"></div>
<?php endif; ?>
<div class="directDownload">
<a href="<?php p($_['downloadURL']); ?>" id="download" class="button">
<a href="<?php p($_['downloadURL']); ?>" id="downloadFile" class="button">
<img class="svg" alt="" src="<?php print_unescaped(OCP\image_path("core", "actions/download.svg")); ?>"/>
<?php p($l->t('Download %s', array($_['filename'])))?> (<?php p($_['fileSize']) ?>)
</a>
@@ -3,8 +3,10 @@
/** @var array $_ */
script('files_sharing', 'settings-personal');
style('files_sharing', 'settings-personal');
script('files_sharing', '3rdparty/gs-share/gs-share');
style('files_sharing', '3rdparty/gs-share/style');
if ($_['showShareIT']) {
script('files_sharing', '3rdparty/gs-share/gs-share');
style('files_sharing', '3rdparty/gs-share/style');
}
?>
<?php if ($_['outgoingServer2serverShareEnabled']): ?>
@@ -18,6 +20,7 @@ style('files_sharing', '3rdparty/gs-share/style');
<br>
<?php if ($_['showShareIT']) {?>
<p>
<?php p($l->t('Share it:')); ?>
<div class="gs-share">
@@ -68,6 +71,7 @@ style('files_sharing', '3rdparty/gs-share/style');
</a></xmp>
</p>
<?php } ?>
</div>
<?php endif; ?>
+11 -11
View File
@@ -78,7 +78,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkPassword() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_enforce_links_password', 'yes', 'yes'],
['core', 'shareapi_enforce_links_password', 'no', 'yes'],
];
$result = $this->getResults($map);
$this->assertArrayHasKey('password', $result['public']);
@@ -89,7 +89,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkNoPassword() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_enforce_links_password', 'yes', 'no'],
['core', 'shareapi_enforce_links_password', 'no', 'no'],
];
$result = $this->getResults($map);
$this->assertArrayHasKey('password', $result['public']);
@@ -100,7 +100,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkNoExpireDate() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'yes', 'no'],
['core', 'shareapi_default_expire_date', 'no', 'no'],
];
$result = $this->getResults($map);
$this->assertArrayHasKey('expire_date', $result['public']);
@@ -111,9 +111,9 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkExpireDate() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'no', 'yes'],
['core', 'shareapi_expire_after_n_days', '7', '7'],
['core', 'shareapi_enforce_expire_date', 'yes', 'no'],
['core', 'shareapi_enforce_expire_date', 'no', 'no'],
];
$result = $this->getResults($map);
$this->assertArrayHasKey('expire_date', $result['public']);
@@ -126,8 +126,8 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkExpireDateEnforced() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'yes', 'yes'],
['core', 'shareapi_enforce_expire_date', 'yes', 'yes'],
['core', 'shareapi_default_expire_date', 'no', 'yes'],
['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
];
$result = $this->getResults($map);
$this->assertArrayHasKey('expire_date', $result['public']);
@@ -138,7 +138,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkSendMail() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_allow_public_notification', 'yes', 'yes'],
['core', 'shareapi_allow_public_notification', 'no', 'yes'],
];
$result = $this->getResults($map);
$this->assertTrue($result['public']['send_mail']);
@@ -147,7 +147,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testLinkNoSendMail() {
$map = [
['core', 'shareapi_allow_links', 'yes', 'yes'],
['core', 'shareapi_allow_public_notification', 'yes', 'no'],
['core', 'shareapi_allow_public_notification', 'no', 'no'],
];
$result = $this->getResults($map);
$this->assertFalse($result['public']['send_mail']);
@@ -155,7 +155,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testUserSendMail() {
$map = [
['core', 'shareapi_allow_mail_notification', 'yes', 'yes'],
['core', 'shareapi_allow_mail_notification', 'no', 'yes'],
];
$result = $this->getResults($map);
$this->assertTrue($result['user']['send_mail']);
@@ -163,7 +163,7 @@ class FilesSharingCapabilitiesTest extends \Test\TestCase {
public function testUserNoSendMail() {
$map = [
['core', 'shareapi_allow_mail_notification', 'yes', 'no'],
['core', 'shareapi_allow_mail_notification', 'no', 'no'],
];
$result = $this->getResults($map);
$this->assertFalse($result['user']['send_mail']);
+14 -5
View File
@@ -266,15 +266,15 @@ class EtagPropagation extends TestCase {
\OCP\Share::unshare(
'folder',
$folderId,
\OCP\Share::SHARE_TYPE_USER,
\OCP\Share::SHARE_TYPE_USER,
self::TEST_FILES_SHARING_API_USER2
)
);
$this->assertEtagsForFoldersChanged([
// direct recipient affected
self::TEST_FILES_SHARING_API_USER2,
self::TEST_FILES_SHARING_API_USER2,
// reshare recipient affected
self::TEST_FILES_SHARING_API_USER4,
self::TEST_FILES_SHARING_API_USER4,
]);
$this->assertAllUnchaged();
@@ -287,9 +287,9 @@ class EtagPropagation extends TestCase {
);
$this->assertEtagsForFoldersChanged([
// direct recipient affected
self::TEST_FILES_SHARING_API_USER2,
self::TEST_FILES_SHARING_API_USER2,
// reshare recipient affected
self::TEST_FILES_SHARING_API_USER4,
self::TEST_FILES_SHARING_API_USER4,
]);
$this->assertAllUnchaged();
@@ -398,4 +398,13 @@ class EtagPropagation extends TestCase {
$this->assertAllUnchaged();
}
public function testRecipientUploadInDirectReshare() {
$this->loginAsUser(self::TEST_FILES_SHARING_API_USER2);
Filesystem::file_put_contents('/directReshare/test.txt', 'sad');
$this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER3]);
$this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER4]);
$this->assertAllUnchaged();
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
/**
* @author Joas Schilling <nickvergessen@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Files_Sharing\Tests\External;
use OCA\Files_Sharing\External\Scanner;
use Test\TestCase;
class ScannerTest extends TestCase {
/** @var \OCA\Files_Sharing\External\Scanner */
protected $scanner;
/** @var \OCA\Files_Sharing\External\Storage|\PHPUnit_Framework_MockObject_MockObject */
protected $storage;
/** @var \OC\Files\Cache\Cache|\PHPUnit_Framework_MockObject_MockObject */
protected $cache;
protected function setUp() {
parent::setUp();
$this->storage = $this->getMockBuilder('\OCA\Files_Sharing\External\Storage')
->disableOriginalConstructor()
->getMock();
$this->cache = $this->getMockBuilder('\OC\Files\Cache\Cache')
->disableOriginalConstructor()
->getMock();
$this->storage->expects($this->any())
->method('getCache')
->willReturn($this->cache);
$this->scanner = new Scanner($this->storage);
}
public function testScanAll() {
$this->storage->expects($this->any())
->method('getShareInfo')
->willReturn(['status' => 'success', 'data' => []]);
// FIXME add real tests, we are currently only checking for
// Declaration of OCA\Files_Sharing\External\Scanner::*() should be
// compatible with OC\Files\Cache\Scanner::*()
$this->scanner->scanAll();
$this->assertTrue(true);
}
public function testScan() {
$this->storage->expects($this->any())
->method('getShareInfo')
->willReturn(['status' => 'success', 'data' => []]);
// FIXME add real tests, we are currently only checking for
// Declaration of OCA\Files_Sharing\External\Scanner::*() should be
// compatible with OC\Files\Cache\Scanner::*()
$this->scanner->scan('test', Scanner::SCAN_RECURSIVE);
$this->assertTrue(true);
}
public function testScanFile() {
// FIXME add real tests, we are currently only checking for
// Declaration of OCA\Files_Sharing\External\Scanner::*() should be
// compatible with OC\Files\Cache\Scanner::*()
$this->scanner->scanFile('test', Scanner::SCAN_RECURSIVE);
$this->assertTrue(true);
}
}
+17
View File
@@ -206,6 +206,23 @@ describe('OCA.Sharing.Util tests', function() {
expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg');
expect($action.find('img').length).toEqual(1);
});
it('do not show static share text when share exists but neither permission nor owner is available', function() {
var $action, $tr;
fileList.setFiles([{
id: 1,
type: 'dir',
name: 'One',
path: '/subdir',
mimetype: 'text/plain',
size: 12,
permissions: OC.PERMISSION_CREATE,
etag: 'abc'
}]);
$tr = fileList.$el.find('tbody tr:first');
expect($tr.find('.action-share').length).toEqual(0);
$action = $tr.find('.action-share-notification');
expect($action.length).toEqual(0);
});
});
describe('Share action', function() {
var showDropDownStub;
+101
View File
@@ -0,0 +1,101 @@
<?php
/**
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <icewind@owncloud.com>
* @author Vincent Petry <pvince81@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\Files_sharing\Tests;
use OC\Files\Filesystem;
use OC\Files\View;
use OC\Lock\MemcacheLockingProvider;
use OCP\Lock\ILockingProvider;
class Locking extends TestCase {
/**
* @var \OC_User_Dummy
*/
private $userBackend;
private $ownerUid;
private $recipientUid;
public function setUp() {
parent::setUp();
$this->userBackend = new \OC_User_Dummy();
\OC::$server->getUserManager()->registerBackend($this->userBackend);
$this->ownerUid = $this->getUniqueID('owner_');
$this->recipientUid = $this->getUniqueID('recipient_');
$this->userBackend->createUser($this->ownerUid, '');
$this->userBackend->createUser($this->recipientUid, '');
$this->loginAsUser($this->ownerUid);
Filesystem::mkdir('/foo');
Filesystem::file_put_contents('/foo/bar.txt', 'asd');
$fileId = Filesystem::getFileInfo('/foo/bar.txt')->getId();
\OCP\Share::shareItem('file', $fileId, \OCP\Share::SHARE_TYPE_USER, $this->recipientUid, 31);
$this->loginAsUser($this->recipientUid);
$this->assertTrue(Filesystem::file_exists('bar.txt'));
}
public function tearDown() {
\OC::$server->getUserManager()->removeBackend($this->userBackend);
parent::tearDown();
}
/**
* @expectedException \OCP\Lock\LockedException
*/
public function testLockAsRecipient() {
$this->loginAsUser($this->ownerUid);
Filesystem::initMountPoints($this->recipientUid);
$recipientView = new View('/' . $this->recipientUid . '/files');
$recipientView->lockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
Filesystem::rename('/foo', '/asd');
}
public function testUnLockAsRecipient() {
$this->loginAsUser($this->ownerUid);
Filesystem::initMountPoints($this->recipientUid);
$recipientView = new View('/' . $this->recipientUid . '/files');
$recipientView->lockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
$recipientView->unlockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
$this->assertTrue(Filesystem::rename('/foo', '/asd'));
}
public function testChangeLock() {
Filesystem::initMountPoints($this->recipientUid);
$recipientView = new View('/' . $this->recipientUid . '/files');
$recipientView->lockFile('bar.txt', ILockingProvider::LOCK_SHARED);
$recipientView->changeLock('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
$recipientView->unlockFile('bar.txt', ILockingProvider::LOCK_EXCLUSIVE);
$this->assertTrue(true);
}
}
+130
View File
@@ -321,4 +321,134 @@ class Test_Files_Sharing extends OCA\Files_sharing\Tests\TestCase {
);
}
/**
* @dataProvider dataProviderGetUsersSharingFile
*
* @param string $groupName name of group to share with
* @param bool $includeOwner whether to include the owner in the result
* @param bool $includePaths whether to include paths in the result
* @param array $expectedResult expected result of the API call
*/
function testGetUsersSharingFile($groupName, $includeOwner, $includePaths, $expectedResult) {
$fileinfo = $this->view->getFileInfo($this->folder);
$result = \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_GROUP,
$groupName, \OCP\Constants::PERMISSION_READ);
$this->assertTrue($result);
// public share
$result = \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_LINK,
null, \OCP\Constants::PERMISSION_READ);
$this->assertNotNull($result); // returns the token!
// owner renames after sharing
$this->view->rename($this->folder, $this->folder . '_owner_renamed');
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
$user2View = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
$user2View->rename($this->folder, $this->folder . '_renamed');
$ownerPath = $this->folder . '_owner_renamed';
$owner = self::TEST_FILES_SHARING_API_USER1;
$result = \OCP\Share::getUsersSharingFile($ownerPath, $owner, $includeOwner, $includePaths);
// sort users to make sure it matches
if ($includePaths) {
ksort($result);
} else {
sort($result['users']);
}
$this->assertEquals(
$expectedResult,
$result
);
}
function dataProviderGetUsersSharingFile() {
// note: "group" contains user1 (the owner), user2 and user3
// and self::TEST_FILES_SHARING_API_GROUP1 contains only user2
return [
// share with group that contains owner
[
'group',
false,
false,
[
'users' =>
[
// because user1 was in group
self::TEST_FILES_SHARING_API_USER1,
self::TEST_FILES_SHARING_API_USER2,
self::TEST_FILES_SHARING_API_USER3,
],
'public' => true,
'remote' => false,
],
],
// share with group that does not contain owner
[
self::TEST_FILES_SHARING_API_GROUP1,
false,
false,
[
'users' =>
[
self::TEST_FILES_SHARING_API_USER2,
],
'public' => true,
'remote' => false,
],
],
// share with group that does not contain owner, include owner
[
self::TEST_FILES_SHARING_API_GROUP1,
true,
false,
[
'users' =>
[
self::TEST_FILES_SHARING_API_USER1,
self::TEST_FILES_SHARING_API_USER2,
],
'public' => true,
'remote' => false,
],
],
// include paths, with owner
[
'group',
true,
true,
[
self::TEST_FILES_SHARING_API_USER1 => self::TEST_FOLDER_NAME . '_owner_renamed',
self::TEST_FILES_SHARING_API_USER2 => self::TEST_FOLDER_NAME . '_renamed',
self::TEST_FILES_SHARING_API_USER3 => self::TEST_FOLDER_NAME,
],
],
// include paths, group without owner
[
self::TEST_FILES_SHARING_API_GROUP1,
false,
true,
[
self::TEST_FILES_SHARING_API_USER2 => self::TEST_FOLDER_NAME. '_renamed',
],
],
// include paths, include owner, group without owner
[
self::TEST_FILES_SHARING_API_GROUP1,
true,
true,
[
self::TEST_FILES_SHARING_API_USER1 => self::TEST_FOLDER_NAME . '_owner_renamed',
self::TEST_FILES_SHARING_API_USER2 => self::TEST_FOLDER_NAME . '_renamed',
],
],
];
}
}
@@ -441,4 +441,43 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase {
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
$this->view->unlink($this->folder);
}
public function testNameConflict() {
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
$view1 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files');
$view1->mkdir('foo');
$folderInfo1 = $view1->getFileInfo('foo');
self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
$view3 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files');
$view3->mkdir('foo');
$folderInfo2 = $view3->getFileInfo('foo');
// share a folder with the same name from two different users to the same user
self::loginHelper(self::TEST_FILES_SHARING_API_USER1);
\OCP\Share::shareItem('folder', $folderInfo1['fileid'], \OCP\Share::SHARE_TYPE_GROUP,
self::TEST_FILES_SHARING_API_GROUP1, 31);
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
self::loginHelper(self::TEST_FILES_SHARING_API_USER3);
\OCP\Share::shareItem('folder', $folderInfo2['fileid'], \OCP\Share::SHARE_TYPE_GROUP,
self::TEST_FILES_SHARING_API_GROUP1, 31);
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
$view2 = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files');
$this->assertTrue($view2->file_exists('/foo'));
$this->assertTrue($view2->file_exists('/foo (2)'));
$mount = $view2->getMount('/foo');
$this->assertInstanceOf('\OCA\Files_Sharing\SharedMount', $mount);
/** @var \OC\Files\Storage\Shared $storage */
$storage = $mount->getStorage();
$source = $storage->getFile('');
$this->assertEquals(self::TEST_FILES_SHARING_API_USER1, $source['uid_owner']);
}
}
+5 -3
View File
@@ -40,7 +40,7 @@ class UnshareChildren extends TestCase {
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
$this->folder = self::TEST_FOLDER_NAME;
$this->subfolder = '/subfolder_share_api_test';
$this->subfolder = '/subfolder_share_api_test';
$this->subsubfolder = '/subsubfolder_share_api_test';
$this->filename = '/share-api-test';
@@ -49,12 +49,14 @@ class UnshareChildren extends TestCase {
$this->view->mkdir($this->folder);
$this->view->mkdir($this->folder . $this->subfolder);
$this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder);
$this->view->file_put_contents($this->folder.$this->filename, $this->data);
$this->view->file_put_contents($this->folder . $this->filename, $this->data);
$this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data);
}
protected function tearDown() {
$this->view->deleteAll($this->folder);
if ($this->view) {
$this->view->deleteAll($this->folder);
}
self::$tempStorage = null;
-1
View File
@@ -18,5 +18,4 @@ To prevent a user from running out of disk space, the ownCloud Deleted files app
<documentation>
<user>user-trashbin</user>
</documentation>
<ocsid>166052</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
0.6.2
0.6.3
+32 -2
View File
@@ -26,6 +26,7 @@ namespace OCA\Files_Trashbin;
use OC\Files\Filesystem;
use OC\Files\Storage\Wrapper\Wrapper;
use OCP\IUserManager;
class Storage extends Wrapper {
@@ -41,8 +42,12 @@ class Storage extends Wrapper {
*/
private static $disableTrash = false;
function __construct($parameters) {
/** @var IUserManager */
private $userManager;
function __construct($parameters, IUserManager $userManager = null) {
$this->mountPoint = $parameters['mountPoint'];
$this->userManager = $userManager;
parent::__construct($parameters);
}
@@ -100,6 +105,27 @@ class Storage extends Wrapper {
return $this->doDelete($path, 'rmdir');
}
/**
* check if it is a file located in data/user/files only files in the
* 'files' directory should be moved to the trash
*
* @param $path
* @return bool
*/
protected function shouldMoveToTrash($path){
$normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path);
$parts = explode('/', $normalized);
if (count($parts) < 4) {
return false;
}
if ($this->userManager->userExists($parts[1]) && $parts[2] == 'files') {
return true;
}
return false;
}
/**
* Run the delete operation with the given method
*
@@ -112,6 +138,7 @@ class Storage extends Wrapper {
if (self::$disableTrash
|| !\OC_App::isEnabled('files_trashbin')
|| (pathinfo($path, PATHINFO_EXTENSION) === 'part')
|| $this->shouldMoveToTrash($path) === false
) {
return call_user_func_array([$this->storage, $method], [$path]);
}
@@ -144,7 +171,10 @@ class Storage extends Wrapper {
*/
public static function setupStorage() {
\OC\Files\Filesystem::addStorageWrapper('oc_trashbin', function ($mountPoint, $storage) {
return new \OCA\Files_Trashbin\Storage(array('storage' => $storage, 'mountPoint' => $mountPoint));
return new \OCA\Files_Trashbin\Storage(
array('storage' => $storage, 'mountPoint' => $mountPoint),
\OC::$server->getUserManager()
);
}, 1);
}
+30
View File
@@ -493,4 +493,34 @@ class Storage extends \Test\TestCase {
$results = $this->rootView->getDirectoryContent($this->user . '/files_trashbin/files/');
$this->assertEquals(0, count($results));
}
/**
* @dataProvider dataTestShouldMoveToTrash
*/
public function testShouldMoveToTrash($mountPoint, $path, $userExists, $expected) {
$tmpStorage = $this->getMockBuilder('\OC\Files\Storage\Temporary')
->disableOriginalConstructor()->getMock();
$userManager = $this->getMockBuilder('OCP\IUserManager')
->disableOriginalConstructor()->getMock();
$userManager->expects($this->any())
->method('userExists')->willReturn($userExists);
$storage = new \OCA\Files_Trashbin\Storage(
['mountPoint' => $mountPoint, 'storage' => $tmpStorage],
$userManager
);
$this->assertSame($expected,
$this->invokePrivate($storage, 'shouldMoveToTrash', [$path])
);
}
public function dataTestShouldMoveToTrash() {
return [
['/schiesbn/', '/files/test.txt', true, true],
['/schiesbn/', '/files/test.txt', false, false],
['/schiesbn/', '/test.txt', true, false],
['/schiesbn/', '/test.txt', false, false],
];
}
}
-1
View File
@@ -18,5 +18,4 @@ In addition to the expiry of versions, ownClouds versions app makes certain n
<user>user-versions</user>
</documentation>
<default_enable/>
<ocsid>166053</ocsid>
</info>
+1 -1
View File
@@ -1 +1 @@
1.0.5
1.0.6
-1
View File
@@ -17,7 +17,6 @@ A user logs into ownCloud with their LDAP or AD credentials, and is granted acce
<documentation>
<admin>admin-ldap</admin>
</documentation>
<ocsid>166061</ocsid>
<dependencies>
<lib>ldap</lib>
</dependencies>
+1 -1
View File
@@ -1 +1 @@
0.6.0
0.6.1
+8 -1
View File
@@ -378,9 +378,16 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
&& intval($this->access->connection->useMemberOfToDetectMembership) === 1
) {
$groupDNs = $this->access->readAttribute($userDN, 'memberOf');
if (is_array($groupDNs)) {
$groupDNs = $this->access->groupsMatchFilter($groupDNs);
foreach ($groupDNs as $dn) {
$groups[] = $this->access->dn2groupname($dn);;
$groupName = $this->access->dn2groupname($dn);
if(is_string($groupName)) {
// be sure to never return false if the dn could not be
// resolved to a name, for whatever reason.
$groups[] = $groupName;
}
}
}
if($primaryGroup !== false) {
+38
View File
@@ -346,6 +346,44 @@ class Access extends LDAPUtility implements user\IUserTools {
return $this->dn2ocname($fdn, $ldapName, false);
}
/**
* accepts an array of group DNs and tests whether they match the user
* filter by doing read operations against the group entries. Returns an
* array of DNs that match the filter.
*
* @param string[] $groupDNs
* @return string[]
*/
public function groupsMatchFilter($groupDNs) {
$validGroupDNs = [];
foreach($groupDNs as $dn) {
$cacheKey = 'groupsMatchFilter-'.$dn;
if($this->connection->isCached($cacheKey)) {
if($this->connection->getFromCache($cacheKey)) {
$validGroupDNs[] = $dn;
}
continue;
}
// Check the base DN first. If this is not met already, we don't
// need to ask the server at all.
if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
$this->connection->writeToCache($cacheKey, false);
continue;
}
$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
if(is_array($result)) {
$this->connection->writeToCache($cacheKey, true);
$validGroupDNs[] = $dn;
} else {
$this->connection->writeToCache($cacheKey, false);
}
}
return $validGroupDNs;
}
/**
* returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
* @param string $dn the dn of the user object
+2 -2
View File
@@ -110,8 +110,8 @@ class Manager {
$user = new User($uid, $dn, $this->access, $this->ocConfig,
$this->ocFilesystem, clone $this->image, $this->ocLog,
$this->avatarManager);
$users['byDN'][$dn] = $user;
$users['byUid'][$uid] = $user;
$this->users['byDN'][$dn] = $user;
$this->users['byUid'][$uid] = $user;
return $user;
}
+7 -1
View File
@@ -342,7 +342,13 @@ class User {
}
$avatar = $this->avatarManager->getAvatar($this->uid);
$avatar->set($this->image);
try {
$avatar->set($this->image);
} catch (\Exception $e) {
\OC::$server->getLogger()->notice(
'Could not set avatar for ' . $this->dn . ', because: ' . $e->getMessage(),
['app' => 'user_ldap']);
}
}
}
+14
View File
@@ -0,0 +1,14 @@
# Generated by ownCloud on 2015-06-18 14:16:40
# line below if for Apache 2.4
<ifModule mod_authz_core.c>
Require all denied
</ifModule>
# line below if for Apache 2.2
<ifModule !mod_authz_core.c>
deny from all
Satisfy All
</ifModule>
# section for Apache 2.2 and 2.4
IndexIgnore *
+4
View File
@@ -404,6 +404,10 @@ class Test_Group_Ldap extends \Test\TestCase {
->method('dn2groupname')
->will($this->returnArgument(0));
$access->expects($this->once())
->method('groupsMatchFilter')
->will($this->returnArgument(0));
$groupBackend = new GroupLDAP($access);
$groups = $groupBackend->getUserGroups('userX');
@@ -0,0 +1,137 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\user_ldap\tests\integration;
use OCA\user_ldap\lib\Access;
use OCA\user_ldap\lib\Connection;
use OCA\user_ldap\lib\LDAP;
use OCA\user_ldap\lib\user\Manager;
abstract class AbstractIntegrationTest {
/** @var LDAP */
protected $ldap;
/** @var Connection */
protected $connection;
/** @var Access */
protected $access;
/** @var Manager */
protected $userManager;
/** @var string */
protected $base;
/** @var string[] */
protected $server;
public function __construct($host, $port, $bind, $pwd, $base) {
$this->base = $base;
$this->server = [
'host' => $host,
'port' => $port,
'dn' => $bind,
'pwd' => $pwd
];
}
/**
* prepares the LDAP environment and sets up a test configuration for
* the LDAP backend.
*/
public function init() {
$this->initLDAPWrapper();
$this->initConnection();
$this->initUserManager();
$this->initAccess();
}
/**
* initializes the test LDAP wrapper
*/
protected function initLDAPWrapper() {
$this->ldap = new LDAP();
}
/**
* sets up the LDAP configuration to be used for the test
*/
protected function initConnection() {
$this->connection = new Connection($this->ldap, '', null);
$this->connection->setConfiguration([
'ldapHost' => $this->server['host'],
'ldapPort' => $this->server['port'],
'ldapBase' => $this->base,
'ldapAgentName' => $this->server['dn'],
'ldapAgentPassword' => $this->server['pwd'],
'ldapUserFilter' => 'objectclass=inetOrgPerson',
'ldapUserDisplayName' => 'cn',
'ldapGroupDisplayName' => 'cn',
'ldapLoginFilter' => '(|(uid=%uid)(samaccountname=%uid))',
'ldapCacheTTL' => 0,
'ldapConfigurationActive' => 1,
]);
}
/**
* initializes an LDAP user manager instance
* @return Manager
*/
protected function initUserManager() {
$this->userManager = new FakeManager();
}
/**
* initializes the Access test instance
*/
protected function initAccess() {
$this->access = new Access($this->connection, $this->ldap, $this->userManager);
}
/**
* runs the test cases while outputting progress and result information
*
* If a test failed, the script is exited with return code 1.
*/
public function run() {
$methods = get_class_methods($this);
$atLeastOneCaseRan = false;
foreach($methods as $method) {
if(strpos($method, 'case') === 0) {
print("running $method " . PHP_EOL);
if(!$this->$method()) {
print(PHP_EOL . '>>> !!! Test ' . $method . ' FAILED !!! <<<' . PHP_EOL . PHP_EOL);
exit(1);
}
$atLeastOneCaseRan = true;
}
}
if($atLeastOneCaseRan) {
print('Tests succeeded' . PHP_EOL);
} else {
print('No Test was available.' . PHP_EOL);
exit(1);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

@@ -0,0 +1,33 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\user_ldap\tests\integration;
/**
* Class FakeManager
*
* this is a mock of \OCA\user_ldap\lib\user\Manager which is a dependency of
* Access, that pulls plenty more things in. Because it is not needed in the
* scope of these tests, we replace it with a mock.
*/
class FakeManager extends \OCA\user_ldap\lib\user\Manager {
public function __construct() {}
}
@@ -0,0 +1,122 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\user_ldap\tests\integration\lib;
use OCA\user_ldap\lib\Connection;
use OCA\user_ldap\tests\integration\AbstractIntegrationTest;
require_once __DIR__ . '/../../../../../lib/base.php';
class IntegrationTestAccessGroupsMatchFilter extends AbstractIntegrationTest {
/**
* prepares the LDAP environment and sets up a test configuration for
* the LDAP backend.
*/
public function init() {
require(__DIR__ . '/../setup-scripts/createExplicitUsers.php');
require(__DIR__ . '/../setup-scripts/createExplicitGroups.php');
require(__DIR__ . '/../setup-scripts/createExplicitGroupsDifferentOU.php');
parent::init();
}
/**
* tests whether the group filter works with one specific group, while the
* input is the same.
*
* @return bool
*/
protected function case1() {
$this->connection->setConfiguration(['ldapGroupFilter' => 'cn=RedGroup']);
$dns = ['cn=RedGroup,ou=Groups,' . $this->base];
$result = $this->access->groupsMatchFilter($dns);
return ($dns === $result);
}
/**
* Tests whether a filter for limited groups is effective when more existing
* groups were passed for validation.
*
* @return bool
*/
protected function case2() {
$this->connection->setConfiguration(['ldapGroupFilter' => '(|(cn=RedGroup)(cn=PurpleGroup))']);
$dns = [
'cn=RedGroup,ou=Groups,' . $this->base,
'cn=BlueGroup,ou=Groups,' . $this->base,
'cn=PurpleGroup,ou=Groups,' . $this->base
];
$result = $this->access->groupsMatchFilter($dns);
$status =
count($result) === 2
&& in_array('cn=RedGroup,ou=Groups,' . $this->base, $result)
&& in_array('cn=PurpleGroup,ou=Groups,' . $this->base, $result);
return $status;
}
/**
* Tests whether a filter for limited groups is effective when more existing
* groups were passed for validation.
*
* @return bool
*/
protected function case3() {
$this->connection->setConfiguration(['ldapGroupFilter' => '(objectclass=groupOfNames)']);
$dns = [
'cn=RedGroup,ou=Groups,' . $this->base,
'cn=PurpleGroup,ou=Groups,' . $this->base,
'cn=SquaredCircleGroup,ou=SpecialGroups,' . $this->base
];
$result = $this->access->groupsMatchFilter($dns);
$status =
count($result) === 2
&& in_array('cn=RedGroup,ou=Groups,' . $this->base, $result)
&& in_array('cn=PurpleGroup,ou=Groups,' . $this->base, $result);
return $status;
}
/**
* sets up the LDAP configuration to be used for the test
*/
protected function initConnection() {
parent::initConnection();
$this->connection->setConfiguration([
'ldapBaseGroups' => 'ou=Groups,' . $this->base,
'ldapUserFilter' => 'objectclass=inetOrgPerson',
'ldapUserDisplayName' => 'displayName',
'ldapGroupDisplayName' => 'cn',
'ldapLoginFilter' => 'uid=%uid',
]);
}
}
require_once(__DIR__ . '/../setup-scripts/config.php');
$test = new IntegrationTestAccessGroupsMatchFilter($host, $port, $adn, $apwd, $bdn);
$test->init();
$test->run();
@@ -0,0 +1,128 @@
<?php
use OCA\user_ldap\lib\user\User;
use OCA\User_LDAP\Mapping\UserMapping;
use OCA\user_ldap\tests\integration\AbstractIntegrationTest;
require_once __DIR__ . '/../../../../../../lib/base.php';
class IntegrationTestUserAvatar extends AbstractIntegrationTest {
/** @var UserMapping */
protected $mapping;
/**
* prepares the LDAP environment and sets up a test configuration for
* the LDAP backend.
*/
public function init() {
require(__DIR__ . '/../../setup-scripts/createExplicitUsers.php');
parent::init();
$this->mapping = new UserMapping(\OC::$server->getDatabaseConnection());
$this->mapping->clear();
$this->access->setUserMapper($this->mapping);
$userBackend = new OCA\user_ldap\USER_LDAP($this->access, \OC::$server->getConfig());
\OC_User::useBackend($userBackend);
}
/**
* A method that does the common steps of test cases 1 and 2. The evaluation
* is not happening here.
*
* @param string $dn
* @param string $username
* @param string $image
*/
private function execFetchTest($dn, $username, $image) {
$this->setJpegPhotoAttribute($dn, $image);
// assigns our self-picked oc username to the dn
$this->mapping->map($dn, $username, 'fakeUUID-' . $username);
// initialize home folder and make sure that the user will update
// also remove an possibly existing avatar
\OC_Util::tearDownFS();
\OC_Util::setupFS($username);
\OC::$server->getUserFolder($username);
\OC::$server->getConfig()->deleteUserValue($username, 'user_ldap', User::USER_PREFKEY_LASTREFRESH);
if(\OC::$server->getAvatarManager()->getAvatar($username)->exists()) {
\OC::$server->getAvatarManager()->getAvatar($username)->remove();
}
// finally attempt to get the avatar set
$user = $this->userManager->get($dn);
$user->updateAvatar();
}
/**
* tests whether an avatar can be retrieved from LDAP and stored correctly
*
* @return bool
*/
protected function case1() {
$image = file_get_contents(__DIR__ . '/../../data/avatar-valid.jpg');
$dn = 'uid=alice,ou=Users,' . $this->base;
$username = 'alice1337';
$this->execFetchTest($dn, $username, $image);
return \OC::$server->getAvatarManager()->getAvatar($username)->exists();
}
/**
* tests whether an image received from LDAP which is of an invalid file
* type is dealt with properly (i.e. not set and not dying).
*
* @return bool
*/
protected function case2() {
// gif by Pmspinner from https://commons.wikimedia.org/wiki/File:Avatar2469_3.gif
$image = file_get_contents(__DIR__ . '/../../data/avatar-invalid.gif');
$dn = 'uid=boris,ou=Users,' . $this->base;
$username = 'boris7844';
$this->execFetchTest($dn, $username, $image);
return !\OC::$server->getAvatarManager()->getAvatar($username)->exists();
}
/**
* This writes an image to the 'jpegPhoto' attribute on LDAP.
*
* @param string $dn
* @param string $image An image read via file_get_contents
* @throws \OC\ServerNotAvailableException
*/
private function setJpegPhotoAttribute($dn, $image) {
$changeSet = ['jpegphoto' => $image];
ldap_mod_add($this->connection->getConnectionResource(), $dn, $changeSet);
}
protected function initUserManager() {
$this->userManager = new \OCA\user_ldap\lib\user\Manager(
\OC::$server->getConfig(),
new \OCA\user_ldap\lib\FilesystemHelper(),
new \OCA\user_ldap\lib\LogWrapper(),
\OC::$server->getAvatarManager(),
new \OCP\Image(),
\OC::$server->getDatabaseConnection()
);
}
/**
* sets up the LDAP configuration to be used for the test
*/
protected function initConnection() {
parent::initConnection();
$this->connection->setConfiguration([
'ldapUserFilter' => 'objectclass=inetOrgPerson',
'ldapUserDisplayName' => 'displayName',
'ldapGroupDisplayName' => 'cn',
'ldapLoginFilter' => 'uid=%uid',
]);
}
}
require_once(__DIR__ . '/../../setup-scripts/config.php');
$test = new IntegrationTestUserAvatar($host, $port, $adn, $apwd, $bdn);
$test->init();
$test->run();
@@ -0,0 +1,60 @@
# Requirements #
Have (as in do copy if not already done) the following files from https://github.com/owncloud/administration/tree/master/ldap-testing copied into the directory "setup-scripts":
* start.sh
* stop.sh
* config.php
Configure config.php according to your needs, also have a look into the LDAP and network settings in start.sh and stop.sh.
# Usage #
The basic command to run a test is:
```# ./run-test.sh [phpscript]```
Yes, run it as root from within this directory.
Example:
```
$ sudo ./run-test.sh lib/IntegrationTestAccessGroupsMatchFilter.php
71cbe88a4993e67066714d71c1cecc5ef26a54911a208103cb6294f90459e574
c74dc0155db4efa7a0515d419528a8727bbc7596601cf25b0df05e348bd74895
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c74dc0155db4 osixia/phpldapadmin:0.5.1 "/sbin/my_init" 1 seconds ago Up Less than a second 80/tcp, 0.0.0.0:8443->443/tcp docker-phpldapadmin
71cbe88a4993 nickstenning/slapd:latest "/sbin/my_init" 1 seconds ago Up Less than a second 127.0.0.1:7770->389/tcp docker-slapd
LDAP server now available under 127.0.0.1:7770 (internal IP is 172.17.0.78)
phpldapadmin now available under https://127.0.0.1:8443
created user : Alice Ealic
created group : RedGroup
created group : BlueGroup
created group : GreenGroup
created group : PurpleGroup
running case1
running case2
Tests succeeded
Stopping and resetting containers
docker-slapd
docker-phpldapadmin
docker-slapd
docker-phpldapadmin
```
# How it works #
1. start.sh is executed which brings up a fresh and clean OpenLDAP in Docker.
2. The provided test script is executed. It also outputs results.
3. stop.sh is executed to shut down OpenLDAP
# Beware #
This is quick solution for basically one test case. With expension this mechanism should be improved as well.
It does not run automatically, unless you do it. No integration with any testing framework.
exceptionOnLostConnection.php is not part of this mechanism. Read its source and run it isolated. While you're at it, port it :þ
+17
View File
@@ -0,0 +1,17 @@
#!/bin/sh
if [ $1 ] ; then
TESTSCRIPT=$1
else
echo "No test file given" exit
fi
if [ ! -e "$TESTSCRIPT" ] ; then
echo "Test file does not exist"
exit
fi
# sleep is necessary, otherwise the LDAP server cannot be connected to, yet.
setup-scripts/start.sh && sleep 5 && php -f "$TESTSCRIPT"
setup-scripts/stop.sh
@@ -0,0 +1,52 @@
<?php
if(php_sapi_name() !== 'cli') {
print('Only via CLI, please.');
exit(1);
}
include __DIR__ . '/config.php';
$cr = ldap_connect($host, $port);
ldap_set_option($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
$ok = ldap_bind($cr, $adn, $apwd);
if (!$ok) {
die(ldap_error($cr));
}
$ouName = 'Groups';
$ouDN = 'ou=' . $ouName . ',' . $bdn;
//creates an OU
if (true) {
$entry = [];
$entry['objectclass'][] = 'top';
$entry['objectclass'][] = 'organizationalunit';
$entry['ou'] = $ouName;
$b = ldap_add($cr, $ouDN, $entry);
if (!$b) {
die(ldap_error($cr));
}
}
$groups = ['RedGroup', 'BlueGroup', 'GreenGroup', 'PurpleGroup'];
// groupOfNames requires groups to have at least one member
// the member used is created by createExplicitUsers.php script
$omniMember = 'uid=alice,ou=Users,' . $bdn;
foreach ($groups as $cn) {
$newDN = 'cn=' . $cn . ',' . $ouDN;
$entry = [];
$entry['cn'] = $cn;
$entry['objectclass'][] = 'groupOfNames';
$entry['member'][] = $omniMember;
$ok = ldap_add($cr, $newDN, $entry);
if ($ok) {
echo('created group ' . ': ' . $entry['cn'] . PHP_EOL);
} else {
die(ldap_error($cr));
}
}
@@ -0,0 +1,52 @@
<?php
if(php_sapi_name() !== 'cli') {
print('Only via CLI, please.');
exit(1);
}
include __DIR__ . '/config.php';
$cr = ldap_connect($host, $port);
ldap_set_option($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
$ok = ldap_bind($cr, $adn, $apwd);
if (!$ok) {
die(ldap_error($cr));
}
$ouName = 'SpecialGroups';
$ouDN = 'ou=' . $ouName . ',' . $bdn;
//creates an OU
if (true) {
$entry = [];
$entry['objectclass'][] = 'top';
$entry['objectclass'][] = 'organizationalunit';
$entry['ou'] = $ouName;
$b = ldap_add($cr, $ouDN, $entry);
if (!$b) {
die(ldap_error($cr));
}
}
$groups = ['SquareGroup', 'CircleGroup', 'TriangleGroup', 'SquaredCircleGroup'];
// groupOfNames requires groups to have at least one member
// the member used is created by createExplicitUsers.php script
$omniMember = 'uid=alice,ou=Users,' . $bdn;
foreach ($groups as $cn) {
$newDN = 'cn=' . $cn . ',' . $ouDN;
$entry = [];
$entry['cn'] = $cn;
$entry['objectclass'][] = 'groupOfNames';
$entry['member'][] = $omniMember;
$ok = ldap_add($cr, $newDN, $entry);
if ($ok) {
echo('created group ' . ': ' . $entry['cn'] . PHP_EOL);
} else {
die(ldap_error($cr));
}
}
@@ -0,0 +1,54 @@
<?php
if(php_sapi_name() !== 'cli') {
print('Only via CLI, please.');
exit(1);
}
include __DIR__ . '/config.php';
$cr = ldap_connect($host, $port);
ldap_set_option($cr, LDAP_OPT_PROTOCOL_VERSION, 3);
$ok = ldap_bind($cr, $adn, $apwd);
if (!$ok) {
die(ldap_error($cr));
}
$ouName = 'Users';
$ouDN = 'ou=' . $ouName . ',' . $bdn;
//creates on OU
if (true) {
$entry = [];
$entry['objectclass'][] = 'top';
$entry['objectclass'][] = 'organizationalunit';
$entry['ou'] = $ouName;
$b = ldap_add($cr, $ouDN, $entry);
if (!$b) {
die(ldap_error($cr));
}
}
$users = ['alice', 'boris'];
foreach ($users as $uid) {
$newDN = 'uid=' . $uid . ',' . $ouDN;
$fn = ucfirst($uid);
$sn = ucfirst(str_shuffle($uid)); // not so explicit but it's OK.
$entry = [];
$entry['cn'] = $fn . ' ' . $sn;
$entry['objectclass'][] = 'inetOrgPerson';
$entry['objectclass'][] = 'person';
$entry['sn'] = $sn;
$entry['userPassword'] = $uid;
$entry['displayName'] = $sn . ', ' . $fn;
$ok = ldap_add($cr, $newDN, $entry);
if ($ok) {
echo('created user ' . ': ' . $entry['cn'] . PHP_EOL);
} else {
die(ldap_error($cr));
}
}

Some files were not shown because too many files have changed in this diff Show More