aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav')
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php1
-rw-r--r--apps/dav/composer/composer/autoload_static.php1
-rw-r--r--apps/dav/l10n/de.js22
-rw-r--r--apps/dav/l10n/de.json22
-rw-r--r--apps/dav/l10n/is.js2
-rw-r--r--apps/dav/l10n/is.json2
-rw-r--r--apps/dav/l10n/nb.js1
-rw-r--r--apps/dav/l10n/nb.json1
-rw-r--r--apps/dav/l10n/nl.js2
-rw-r--r--apps/dav/l10n/nl.json2
-rw-r--r--apps/dav/l10n/zh_CN.js2
-rw-r--r--apps/dav/l10n/zh_CN.json2
-rw-r--r--apps/dav/lib/Connector/Sabre/FilesPlugin.php6
-rw-r--r--apps/dav/lib/Connector/Sabre/Node.php26
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php6
-rw-r--r--apps/dav/lib/Controller/DirectController.php17
-rw-r--r--apps/dav/lib/DAV/Sharing/Backend.php2
-rw-r--r--apps/dav/lib/DAV/ViewOnlyPlugin.php108
-rw-r--r--apps/dav/lib/Server.php6
-rw-r--r--apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php14
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/NodeTest.php62
-rw-r--r--apps/dav/tests/unit/Controller/DirectControllerTest.php26
-rw-r--r--apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php117
23 files changed, 410 insertions, 40 deletions
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index b01ae68e43a..d3290c4e792 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -191,6 +191,7 @@ return array(
'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => $baseDir . '/../lib/DAV/Sharing/Xml/Invite.php',
'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => $baseDir . '/../lib/DAV/Sharing/Xml/ShareRequest.php',
'OCA\\DAV\\DAV\\SystemPrincipalBackend' => $baseDir . '/../lib/DAV/SystemPrincipalBackend.php',
+ 'OCA\\DAV\\DAV\\ViewOnlyPlugin' => $baseDir . '/../lib/DAV/ViewOnlyPlugin.php',
'OCA\\DAV\\Db\\Direct' => $baseDir . '/../lib/Db/Direct.php',
'OCA\\DAV\\Db\\DirectMapper' => $baseDir . '/../lib/Db/DirectMapper.php',
'OCA\\DAV\\Direct\\DirectFile' => $baseDir . '/../lib/Direct/DirectFile.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 4c9a1dcc793..4d425f70f3b 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -206,6 +206,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/Invite.php',
'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/ShareRequest.php',
'OCA\\DAV\\DAV\\SystemPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/SystemPrincipalBackend.php',
+ 'OCA\\DAV\\DAV\\ViewOnlyPlugin' => __DIR__ . '/..' . '/../lib/DAV/ViewOnlyPlugin.php',
'OCA\\DAV\\Db\\Direct' => __DIR__ . '/..' . '/../lib/Db/Direct.php',
'OCA\\DAV\\Db\\DirectMapper' => __DIR__ . '/..' . '/../lib/Db/DirectMapper.php',
'OCA\\DAV\\Direct\\DirectFile' => __DIR__ . '/..' . '/../lib/Direct/DirectFile.php',
diff --git a/apps/dav/l10n/de.js b/apps/dav/l10n/de.js
index 98e15bafb03..e7e0695f4d0 100644
--- a/apps/dav/l10n/de.js
+++ b/apps/dav/l10n/de.js
@@ -14,10 +14,10 @@ OC.L10N.register(
"You restored calendar {calendar}" : "Du hast den Kalender {calendar} wiederhergestellt",
"You shared calendar {calendar} as public link" : "Du hast den Kalender {calendar} als öffentlichen Link geteilt",
"You removed public link for calendar {calendar}" : "Du hast den öffentlichen Link für Kalender {calendar} entfernt",
- "{actor} shared calendar {calendar} with you" : "{actor} hat den Kalender {calendar} mit Dir geteilt",
+ "{actor} shared calendar {calendar} with you" : "{actor} hat den Kalender {calendar} mit dir geteilt",
"You shared calendar {calendar} with {user}" : "Du hast den Kalender {calendar} mit {user} geteilt",
"{actor} shared calendar {calendar} with {user}" : "{actor} hat den Kalender {calendar} mit {user} geteilt",
- "{actor} unshared calendar {calendar} from you" : "{actor} teilt den Kalender {calendar} nicht mehr mit Dir",
+ "{actor} unshared calendar {calendar} from you" : "{actor} teilt den Kalender {calendar} nicht mehr mit dir",
"You unshared calendar {calendar} from {user}" : "Du teilst den Kalender {calendar} nicht mehr mit {user}",
"{actor} unshared calendar {calendar} from {user}" : "{actor} teilt den Kalender {calendar} nicht mehr mit {user}",
"{actor} unshared calendar {calendar} from themselves" : "{actor} teilt den Kalender {calendar} nicht mehr mit sich selbst",
@@ -34,7 +34,7 @@ OC.L10N.register(
"You updated event {event} in calendar {calendar}" : "Du hast den Termin {event} im Kalender {calendar} aktualisiert",
"{actor} moved event {event} from calendar {sourceCalendar} to calendar {targetCalendar}" : "{actor} hat das Ereignis {event} vom Kalender {sourceCalendar} in den Kalender {targetCalendar} verschoben",
"You moved event {event} from calendar {sourceCalendar} to calendar {targetCalendar}" : "Du hast das Ereignis {event} vom Kalender {sourceCalendar} in den Kalender {targetCalendar} verschoben",
- "{actor} restored event {event} of calendar {calendar}" : "{actor} hat das Adressbuch {addressbook} mit Dir geteilt",
+ "{actor} restored event {event} of calendar {calendar}" : "{actor} hat das Adressbuch {addressbook} mit dir geteilt",
"You restored event {event} of calendar {calendar}" : "Du hast den Termin {event} im Kalender {calendar} wiederhergestellt",
"Busy" : "Beschäftigt",
"{actor} created to-do {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} erstellt",
@@ -94,13 +94,13 @@ OC.L10N.register(
"You deleted address book {addressbook}" : "Du hast das Adressbuch {addressbook} gelöscht",
"{actor} updated address book {addressbook}" : "{actor} hat das Adressbuch {addressbook} aktualisiert",
"You updated address book {addressbook}" : "Du hast das Adressbuch {addressbook} aktualisiert",
- "{actor} shared address book {addressbook} with you" : "{actor} hat das Adressbuch {addressbook} mit Dir geteilt",
+ "{actor} shared address book {addressbook} with you" : "{actor} hat das Adressbuch {addressbook} mit dir geteilt",
"You shared address book {addressbook} with {user}" : "Du hast das Adressbuch {addressbook} geteilt",
"{actor} shared address book {addressbook} with {user}" : "{actor} hat das Adressbuch {addressbook} mit {user} geteilt",
- "{actor} unshared address book {addressbook} from you" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit Dir",
+ "{actor} unshared address book {addressbook} from you" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit dir.",
"You unshared address book {addressbook} from {user}" : "Du teilst das Adressbuch {addressbook} nicht mehr mit {user}",
"{actor} unshared address book {addressbook} from {user}" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit {user}",
- "{actor} unshared address book {addressbook} from themselves" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit Dir",
+ "{actor} unshared address book {addressbook} from themselves" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit dir.",
"You shared address book {addressbook} with group {group}" : "Du hast das Adressbuch {addressbook} mit der Gruppe {group} geteilt",
"{actor} shared address book {addressbook} with group {group}" : "{actor} hat das Adressbuch {addressbook} mit der Gruppe {group} geteilt",
"You unshared address book {addressbook} from group {group}" : "Du teilst das Adressbuch {addressbook} nicht mehr mit der Gruppe {group}",
@@ -146,7 +146,7 @@ OC.L10N.register(
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV-Endpunkt",
"Availability" : "Verfügbarkeit",
- "If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Wenn Du Deine Arbeitszeiten konfigurierst, können andere Benutzer sehen, wann Du nicht im Büro bist, wenn sie eine Besprechung buchen.",
+ "If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Wenn du deine Arbeitszeiten konfigurierst, können andere Benutzer sehen, wann du nicht im Büro bist, wenn sie eine Besprechung buchen.",
"Time zone:" : "Zeitzone:",
"to" : "an",
"Delete slot" : "Slot löschen",
@@ -159,7 +159,7 @@ OC.L10N.register(
"Friday" : "Freitag",
"Saturday" : "Samstag",
"Sunday" : "Sonntag",
- "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setze den Benutzerstatus automatisch auf „Nicht stören“, wenn Du nicht erreichbar bist, um alle Benachrichtigungen stumm zu schalten.",
+ "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setze den Benutzerstatus automatisch auf „Nicht stören“, wenn du nicht erreichbar bist, um alle Benachrichtigungen stumm zu schalten.",
"Save" : "Speichern",
"Failed to load availability" : "Fehler beim Laden der Verfügbarkeit",
"Saved availability" : "Verfügbarkeit gespeichert",
@@ -174,9 +174,9 @@ OC.L10N.register(
"Send reminder notifications to calendar sharees as well" : "Erinnerungsbenachrichtigungen auch an die freigegebenen Kalender senden",
"Reminders are always sent to organizers and attendees." : "Erinnerungen werden immer an Organisatoren und Teilnehmer gesendet.",
"Enable notifications for events via push" : "Benachrichtigungen für Termine per Push aktivieren",
- "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installiere außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinde Deinen Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.",
- "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Bitte stelle sicher, dass Du {emailopen}den E-Mail Server{linkclose} ordnungsgemäß einrichtest.",
- "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren Deines Teilnehmerstatus aufgetreten.",
+ "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installiere außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinde deinen Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.",
+ "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Bitte stelle sicher, dass du {emailopen}den E-Mail Server{linkclose} ordnungsgemäß einrichtest.",
+ "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren deines Teilnehmerstatus aufgetreten.",
"Please contact the organizer directly." : "Bitte den Organisator direkt kontaktieren.",
"Are you accepting the invitation?" : "Die Einladung annehmen?",
"Tentative" : "Vorläufig",
diff --git a/apps/dav/l10n/de.json b/apps/dav/l10n/de.json
index 5712276fe83..0bdb670759e 100644
--- a/apps/dav/l10n/de.json
+++ b/apps/dav/l10n/de.json
@@ -12,10 +12,10 @@
"You restored calendar {calendar}" : "Du hast den Kalender {calendar} wiederhergestellt",
"You shared calendar {calendar} as public link" : "Du hast den Kalender {calendar} als öffentlichen Link geteilt",
"You removed public link for calendar {calendar}" : "Du hast den öffentlichen Link für Kalender {calendar} entfernt",
- "{actor} shared calendar {calendar} with you" : "{actor} hat den Kalender {calendar} mit Dir geteilt",
+ "{actor} shared calendar {calendar} with you" : "{actor} hat den Kalender {calendar} mit dir geteilt",
"You shared calendar {calendar} with {user}" : "Du hast den Kalender {calendar} mit {user} geteilt",
"{actor} shared calendar {calendar} with {user}" : "{actor} hat den Kalender {calendar} mit {user} geteilt",
- "{actor} unshared calendar {calendar} from you" : "{actor} teilt den Kalender {calendar} nicht mehr mit Dir",
+ "{actor} unshared calendar {calendar} from you" : "{actor} teilt den Kalender {calendar} nicht mehr mit dir",
"You unshared calendar {calendar} from {user}" : "Du teilst den Kalender {calendar} nicht mehr mit {user}",
"{actor} unshared calendar {calendar} from {user}" : "{actor} teilt den Kalender {calendar} nicht mehr mit {user}",
"{actor} unshared calendar {calendar} from themselves" : "{actor} teilt den Kalender {calendar} nicht mehr mit sich selbst",
@@ -32,7 +32,7 @@
"You updated event {event} in calendar {calendar}" : "Du hast den Termin {event} im Kalender {calendar} aktualisiert",
"{actor} moved event {event} from calendar {sourceCalendar} to calendar {targetCalendar}" : "{actor} hat das Ereignis {event} vom Kalender {sourceCalendar} in den Kalender {targetCalendar} verschoben",
"You moved event {event} from calendar {sourceCalendar} to calendar {targetCalendar}" : "Du hast das Ereignis {event} vom Kalender {sourceCalendar} in den Kalender {targetCalendar} verschoben",
- "{actor} restored event {event} of calendar {calendar}" : "{actor} hat das Adressbuch {addressbook} mit Dir geteilt",
+ "{actor} restored event {event} of calendar {calendar}" : "{actor} hat das Adressbuch {addressbook} mit dir geteilt",
"You restored event {event} of calendar {calendar}" : "Du hast den Termin {event} im Kalender {calendar} wiederhergestellt",
"Busy" : "Beschäftigt",
"{actor} created to-do {todo} in list {calendar}" : "{actor} hat die Aufgabe {todo} in der Liste {calendar} erstellt",
@@ -92,13 +92,13 @@
"You deleted address book {addressbook}" : "Du hast das Adressbuch {addressbook} gelöscht",
"{actor} updated address book {addressbook}" : "{actor} hat das Adressbuch {addressbook} aktualisiert",
"You updated address book {addressbook}" : "Du hast das Adressbuch {addressbook} aktualisiert",
- "{actor} shared address book {addressbook} with you" : "{actor} hat das Adressbuch {addressbook} mit Dir geteilt",
+ "{actor} shared address book {addressbook} with you" : "{actor} hat das Adressbuch {addressbook} mit dir geteilt",
"You shared address book {addressbook} with {user}" : "Du hast das Adressbuch {addressbook} geteilt",
"{actor} shared address book {addressbook} with {user}" : "{actor} hat das Adressbuch {addressbook} mit {user} geteilt",
- "{actor} unshared address book {addressbook} from you" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit Dir",
+ "{actor} unshared address book {addressbook} from you" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit dir.",
"You unshared address book {addressbook} from {user}" : "Du teilst das Adressbuch {addressbook} nicht mehr mit {user}",
"{actor} unshared address book {addressbook} from {user}" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit {user}",
- "{actor} unshared address book {addressbook} from themselves" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit Dir",
+ "{actor} unshared address book {addressbook} from themselves" : "{actor} teilt das Adressbuch {addressbook} nicht mehr mit dir.",
"You shared address book {addressbook} with group {group}" : "Du hast das Adressbuch {addressbook} mit der Gruppe {group} geteilt",
"{actor} shared address book {addressbook} with group {group}" : "{actor} hat das Adressbuch {addressbook} mit der Gruppe {group} geteilt",
"You unshared address book {addressbook} from group {group}" : "Du teilst das Adressbuch {addressbook} nicht mehr mit der Gruppe {group}",
@@ -144,7 +144,7 @@
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV-Endpunkt",
"Availability" : "Verfügbarkeit",
- "If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Wenn Du Deine Arbeitszeiten konfigurierst, können andere Benutzer sehen, wann Du nicht im Büro bist, wenn sie eine Besprechung buchen.",
+ "If you configure your working hours, other users will see when you are out of office when they book a meeting." : "Wenn du deine Arbeitszeiten konfigurierst, können andere Benutzer sehen, wann du nicht im Büro bist, wenn sie eine Besprechung buchen.",
"Time zone:" : "Zeitzone:",
"to" : "an",
"Delete slot" : "Slot löschen",
@@ -157,7 +157,7 @@
"Friday" : "Freitag",
"Saturday" : "Samstag",
"Sunday" : "Sonntag",
- "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setze den Benutzerstatus automatisch auf „Nicht stören“, wenn Du nicht erreichbar bist, um alle Benachrichtigungen stumm zu schalten.",
+ "Automatically set user status to \"Do not disturb\" outside of availability to mute all notifications." : "Setze den Benutzerstatus automatisch auf „Nicht stören“, wenn du nicht erreichbar bist, um alle Benachrichtigungen stumm zu schalten.",
"Save" : "Speichern",
"Failed to load availability" : "Fehler beim Laden der Verfügbarkeit",
"Saved availability" : "Verfügbarkeit gespeichert",
@@ -172,9 +172,9 @@
"Send reminder notifications to calendar sharees as well" : "Erinnerungsbenachrichtigungen auch an die freigegebenen Kalender senden",
"Reminders are always sent to organizers and attendees." : "Erinnerungen werden immer an Organisatoren und Teilnehmer gesendet.",
"Enable notifications for events via push" : "Benachrichtigungen für Termine per Push aktivieren",
- "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installiere außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinde Deinen Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.",
- "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Bitte stelle sicher, dass Du {emailopen}den E-Mail Server{linkclose} ordnungsgemäß einrichtest.",
- "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren Deines Teilnehmerstatus aufgetreten.",
+ "Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "Installiere außerdem die {calendarappstoreopen}Kalender-App{linkclose} oder {calendardocopen}verbinde deinen Desktop & Mobilgerät zur Synchronisierung ↗{linkclose}.",
+ "Please make sure to properly set up {emailopen}the email server{linkclose}." : "Bitte stelle sicher, dass du {emailopen}den E-Mail Server{linkclose} ordnungsgemäß einrichtest.",
+ "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren deines Teilnehmerstatus aufgetreten.",
"Please contact the organizer directly." : "Bitte den Organisator direkt kontaktieren.",
"Are you accepting the invitation?" : "Die Einladung annehmen?",
"Tentative" : "Vorläufig",
diff --git a/apps/dav/l10n/is.js b/apps/dav/l10n/is.js
index 9d16ae97f91..676b58c83ee 100644
--- a/apps/dav/l10n/is.js
+++ b/apps/dav/l10n/is.js
@@ -51,6 +51,7 @@ OC.L10N.register(
"%1$s via %2$s" : "%1$s með %2$s",
"Invitation canceled" : "Hætt við boð",
"Invitation updated" : "Boð uppfært",
+ "Time:" : "Tími:",
"Location:" : "Staðsetning:",
"Link:" : "Tengill:",
"Accept" : "Samþykkja",
@@ -66,6 +67,7 @@ OC.L10N.register(
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV-endapunktur",
"to" : "til",
+ "Delete slot" : "Eyða tímahólfi",
"Monday" : "Mánudagur",
"Tuesday" : "Þriðjudagur",
"Wednesday" : "Miðvikudagur",
diff --git a/apps/dav/l10n/is.json b/apps/dav/l10n/is.json
index 29ba35dc18c..a5a5ac796a4 100644
--- a/apps/dav/l10n/is.json
+++ b/apps/dav/l10n/is.json
@@ -49,6 +49,7 @@
"%1$s via %2$s" : "%1$s með %2$s",
"Invitation canceled" : "Hætt við boð",
"Invitation updated" : "Boð uppfært",
+ "Time:" : "Tími:",
"Location:" : "Staðsetning:",
"Link:" : "Tengill:",
"Accept" : "Samþykkja",
@@ -64,6 +65,7 @@
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV-endapunktur",
"to" : "til",
+ "Delete slot" : "Eyða tímahólfi",
"Monday" : "Mánudagur",
"Tuesday" : "Þriðjudagur",
"Wednesday" : "Miðvikudagur",
diff --git a/apps/dav/l10n/nb.js b/apps/dav/l10n/nb.js
index 06279d90460..c0a92521f9a 100644
--- a/apps/dav/l10n/nb.js
+++ b/apps/dav/l10n/nb.js
@@ -77,6 +77,7 @@ OC.L10N.register(
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV endepunkt",
"to" : "til",
+ "Delete slot" : "Slett tidsluke",
"Monday" : "Mandag",
"Tuesday" : "Tirsdag",
"Wednesday" : "Onsdag",
diff --git a/apps/dav/l10n/nb.json b/apps/dav/l10n/nb.json
index 9e8f78bddac..d60fa424bd7 100644
--- a/apps/dav/l10n/nb.json
+++ b/apps/dav/l10n/nb.json
@@ -75,6 +75,7 @@
"WebDAV" : "WebDAV",
"WebDAV endpoint" : "WebDAV endepunkt",
"to" : "til",
+ "Delete slot" : "Slett tidsluke",
"Monday" : "Mandag",
"Tuesday" : "Tirsdag",
"Wednesday" : "Onsdag",
diff --git a/apps/dav/l10n/nl.js b/apps/dav/l10n/nl.js
index 2d466feeb98..3ebb608547f 100644
--- a/apps/dav/l10n/nl.js
+++ b/apps/dav/l10n/nl.js
@@ -82,7 +82,7 @@ OC.L10N.register(
"More options at %s" : "Meer opties op %s",
"Contacts" : "Contactpersonen",
"{actor} created address book {addressbook}" : "{actor} creëerde adresboek {addressbook}",
- "You created address book {addressbook}" : "Je creëere adresboek {addressbook}",
+ "You created address book {addressbook}" : "Je creëerde adresboek {addressbook}",
"{actor} deleted address book {addressbook}" : "{actor} verwijdede adresboek {addressbook}",
"You deleted address book {addressbook}" : "Je verwijderde adresboek {addressbook}",
"{actor} updated address book {addressbook}" : "{actor} wijzigde adresboek {addressbook}",
diff --git a/apps/dav/l10n/nl.json b/apps/dav/l10n/nl.json
index 13b40b33bdc..74d32b8ea8b 100644
--- a/apps/dav/l10n/nl.json
+++ b/apps/dav/l10n/nl.json
@@ -80,7 +80,7 @@
"More options at %s" : "Meer opties op %s",
"Contacts" : "Contactpersonen",
"{actor} created address book {addressbook}" : "{actor} creëerde adresboek {addressbook}",
- "You created address book {addressbook}" : "Je creëere adresboek {addressbook}",
+ "You created address book {addressbook}" : "Je creëerde adresboek {addressbook}",
"{actor} deleted address book {addressbook}" : "{actor} verwijdede adresboek {addressbook}",
"You deleted address book {addressbook}" : "Je verwijderde adresboek {addressbook}",
"{actor} updated address book {addressbook}" : "{actor} wijzigde adresboek {addressbook}",
diff --git a/apps/dav/l10n/zh_CN.js b/apps/dav/l10n/zh_CN.js
index 29a4546fe98..8a8c9330513 100644
--- a/apps/dav/l10n/zh_CN.js
+++ b/apps/dav/l10n/zh_CN.js
@@ -132,6 +132,8 @@ OC.L10N.register(
"Hence they will not be available immediately after enabling but will show up after some time." : "因此,它们在启用后不会立即可用,但会在一段时间后显示出来。",
"Send notifications for events" : "发送事件通知",
"Notifications are sent via background jobs, so these must occur often enough." : "通知将通过后台任务发送,所以任务的频率要足够高。",
+ "Send reminder notifications to calendar sharees as well" : "同时向日历共享者发送提醒通知",
+ "Reminders are always sent to organizers and attendees." : "始终向组织者和与会者发出提醒。",
"Enable notifications for events via push" : "启用推送事件通知",
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "也安装 {calendarappstoreopen}日历应用{linkclose},或者 {calendardocopen}连接您的桌面和移动端同步日历 ↗{linkclose}。",
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "请确保正确设置 {emailopen}邮件服务器{linkclose}。",
diff --git a/apps/dav/l10n/zh_CN.json b/apps/dav/l10n/zh_CN.json
index e2109499ab2..9f69313b98e 100644
--- a/apps/dav/l10n/zh_CN.json
+++ b/apps/dav/l10n/zh_CN.json
@@ -130,6 +130,8 @@
"Hence they will not be available immediately after enabling but will show up after some time." : "因此,它们在启用后不会立即可用,但会在一段时间后显示出来。",
"Send notifications for events" : "发送事件通知",
"Notifications are sent via background jobs, so these must occur often enough." : "通知将通过后台任务发送,所以任务的频率要足够高。",
+ "Send reminder notifications to calendar sharees as well" : "同时向日历共享者发送提醒通知",
+ "Reminders are always sent to organizers and attendees." : "始终向组织者和与会者发出提醒。",
"Enable notifications for events via push" : "启用推送事件通知",
"Also install the {calendarappstoreopen}Calendar app{linkclose}, or {calendardocopen}connect your desktop & mobile for syncing ↗{linkclose}." : "也安装 {calendarappstoreopen}日历应用{linkclose},或者 {calendardocopen}连接您的桌面和移动端同步日历 ↗{linkclose}。",
"Please make sure to properly set up {emailopen}the email server{linkclose}." : "请确保正确设置 {emailopen}邮件服务器{linkclose}。",
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
index b784764f8fe..e9d27d4e7f6 100644
--- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
@@ -65,6 +65,7 @@ class FilesPlugin extends ServerPlugin {
public const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
public const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
public const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
+ public const SHARE_ATTRIBUTES_PROPERTYNAME = '{http://nextcloud.org/ns}share-attributes';
public const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
public const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
public const GETETAG_PROPERTYNAME = '{DAV:}getetag';
@@ -134,6 +135,7 @@ class FilesPlugin extends ServerPlugin {
$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
$server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
+ $server->protectedProperties[] = self::SHARE_ATTRIBUTES_PROPERTYNAME;
$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
@@ -321,6 +323,10 @@ class FilesPlugin extends ServerPlugin {
return json_encode($ocmPermissions);
});
+ $propFind->handle(self::SHARE_ATTRIBUTES_PROPERTYNAME, function () use ($node, $httpRequest) {
+ return json_encode($node->getShareAttributes());
+ });
+
$propFind->handle(self::GETETAG_PROPERTYNAME, function () use ($node): string {
return $node->getETag();
});
diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php
index e4517068f42..87f2fea394f 100644
--- a/apps/dav/lib/Connector/Sabre/Node.php
+++ b/apps/dav/lib/Connector/Sabre/Node.php
@@ -38,6 +38,7 @@ namespace OCA\DAV\Connector\Sabre;
use OC\Files\Mount\MoveableMount;
use OC\Files\Node\File;
use OC\Files\Node\Folder;
+use OC\Files\Storage\Wrapper\Wrapper;
use OC\Files\View;
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
use OCP\Files\FileInfo;
@@ -323,6 +324,31 @@ abstract class Node implements \Sabre\DAV\INode {
}
/**
+ * @return array
+ */
+ public function getShareAttributes(): array {
+ $attributes = [];
+
+ try {
+ $storage = $this->info->getStorage();
+ } catch (StorageNotAvailableException $e) {
+ $storage = null;
+ }
+
+ if ($storage && $storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
+ /** @var \OCA\Files_Sharing\SharedStorage $storage */
+ $attributes = $storage->getShare()->getAttributes();
+ if ($attributes === null) {
+ return [];
+ } else {
+ return $attributes->toArray();
+ }
+ }
+
+ return $attributes;
+ }
+
+ /**
* @param string $user
* @return string
*/
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index 8f1f710ca5e..4c57f3412e3 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -33,6 +33,7 @@ namespace OCA\DAV\Connector\Sabre;
use OCP\Files\Folder;
use OCA\DAV\AppInfo\PluginManager;
+use OCA\DAV\DAV\ViewOnlyPlugin;
use OCA\DAV\Files\BrowserErrorPagePlugin;
use OCP\Files\Mount\IMountManager;
use OCP\IConfig;
@@ -158,6 +159,11 @@ class ServerFactory {
$server->addPlugin(new \OCA\DAV\Connector\Sabre\QuotaPlugin($view, true));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\ChecksumUpdatePlugin());
+ // Allow view-only plugin for webdav requests
+ $server->addPlugin(new ViewOnlyPlugin(
+ $this->logger
+ ));
+
if ($this->userSession->isLoggedIn()) {
$server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\SharesPlugin(
diff --git a/apps/dav/lib/Controller/DirectController.php b/apps/dav/lib/Controller/DirectController.php
index 955400998cf..f9c83488935 100644
--- a/apps/dav/lib/Controller/DirectController.php
+++ b/apps/dav/lib/Controller/DirectController.php
@@ -31,8 +31,12 @@ use OCA\DAV\Db\DirectMapper;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSNotFoundException;
+use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\EventDispatcher\GenericEvent;
+use OCP\EventDispatcher\IEventDispatcher;
+use OCP\Files\Events\BeforeDirectFileDownloadEvent;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\IRequest;
@@ -59,6 +63,8 @@ class DirectController extends OCSController {
/** @var IURLGenerator */
private $urlGenerator;
+ /** @var IEventDispatcher */
+ private $eventDispatcher;
public function __construct(string $appName,
IRequest $request,
@@ -67,7 +73,8 @@ class DirectController extends OCSController {
DirectMapper $mapper,
ISecureRandom $random,
ITimeFactory $timeFactory,
- IURLGenerator $urlGenerator) {
+ IURLGenerator $urlGenerator,
+ IEventDispatcher $eventDispatcher) {
parent::__construct($appName, $request);
$this->rootFolder = $rootFolder;
@@ -76,6 +83,7 @@ class DirectController extends OCSController {
$this->random = $random;
$this->timeFactory = $timeFactory;
$this->urlGenerator = $urlGenerator;
+ $this->eventDispatcher = $eventDispatcher;
}
/**
@@ -99,6 +107,13 @@ class DirectController extends OCSController {
throw new OCSBadRequestException('Direct download only works for files');
}
+ $event = new BeforeDirectFileDownloadEvent($userFolder->getRelativePath($file->getPath()));
+ $this->eventDispatcher->dispatchTyped($event);
+
+ if ($event->isSuccessful() === false) {
+ throw new OCSForbiddenException('Permission denied to download file');
+ }
+
//TODO: at some point we should use the directdownlaod function of storages
$direct = new Direct();
$direct->setUserId($this->userId);
diff --git a/apps/dav/lib/DAV/Sharing/Backend.php b/apps/dav/lib/DAV/Sharing/Backend.php
index 92971992c20..90d2c7ebf82 100644
--- a/apps/dav/lib/DAV/Sharing/Backend.php
+++ b/apps/dav/lib/DAV/Sharing/Backend.php
@@ -179,7 +179,7 @@ class Backend {
while ($row = $result->fetch()) {
$p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
$shares[] = [
- 'href' => "principal:${row['principaluri']}",
+ 'href' => "principal:{$row['principaluri']}",
'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '',
'status' => 1,
'readOnly' => (int) $row['access'] === self::ACCESS_READ,
diff --git a/apps/dav/lib/DAV/ViewOnlyPlugin.php b/apps/dav/lib/DAV/ViewOnlyPlugin.php
new file mode 100644
index 00000000000..1504969b5b4
--- /dev/null
+++ b/apps/dav/lib/DAV/ViewOnlyPlugin.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * @author Piotr Mrowczynski piotr@owncloud.com
+ *
+ * @copyright Copyright (c) 2019, ownCloud GmbH
+ * @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\DAV\DAV;
+
+use OCA\DAV\Connector\Sabre\Exception\Forbidden;
+use OCA\DAV\Connector\Sabre\File as DavFile;
+use OCA\DAV\Meta\MetaFile;
+use OCP\Files\FileInfo;
+use OCP\Files\NotFoundException;
+use Psr\Log\LoggerInterface;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Sabre\HTTP\RequestInterface;
+use Sabre\DAV\Exception\NotFound;
+
+/**
+ * Sabre plugin for restricting file share receiver download:
+ */
+class ViewOnlyPlugin extends ServerPlugin {
+
+ private ?Server $server = null;
+ private LoggerInterface $logger;
+
+ public function __construct(LoggerInterface $logger) {
+ $this->logger = $logger;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ */
+ public function initialize(Server $server): void {
+ $this->server = $server;
+ //priority 90 to make sure the plugin is called before
+ //Sabre\DAV\CorePlugin::httpGet
+ $this->server->on('method:GET', [$this, 'checkViewOnly'], 90);
+ }
+
+ /**
+ * Disallow download via DAV Api in case file being received share
+ * and having special permission
+ *
+ * @throws Forbidden
+ * @throws NotFoundException
+ */
+ public function checkViewOnly(RequestInterface $request): bool {
+ $path = $request->getPath();
+
+ try {
+ assert($this->server !== null);
+ $davNode = $this->server->tree->getNodeForPath($path);
+ if (!($davNode instanceof DavFile)) {
+ return true;
+ }
+ // Restrict view-only to nodes which are shared
+ $node = $davNode->getNode();
+
+ $storage = $node->getStorage();
+
+ if (!$storage->instanceOfStorage(\OCA\Files_Sharing\SharedStorage::class)) {
+ return true;
+ }
+ // Extract extra permissions
+ /** @var \OCA\Files_Sharing\SharedStorage $storage */
+ $share = $storage->getShare();
+
+ $attributes = $share->getAttributes();
+ if ($attributes === null) {
+ return true;
+ }
+
+ // Check if read-only and on whether permission can download is both set and disabled.
+ $canDownload = $attributes->getAttribute('permissions', 'download');
+ if ($canDownload !== null && !$canDownload) {
+ throw new Forbidden('Access to this resource has been denied because it is in view-only mode.');
+ }
+ } catch (NotFound $e) {
+ $this->logger->warning($e->getMessage(), [
+ 'exception' => $e,
+ ]);
+ }
+
+ return true;
+ }
+}
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 5b532465aba..2cfcb3f5393 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -62,6 +62,7 @@ use OCA\DAV\Connector\Sabre\SharesPlugin;
use OCA\DAV\Connector\Sabre\TagsPlugin;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\DAV\PublicAuth;
+use OCA\DAV\DAV\ViewOnlyPlugin;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\DAV\Files\BrowserErrorPagePlugin;
use OCA\DAV\Files\LazySearchBackend;
@@ -229,6 +230,11 @@ class Server {
$this->server->addPlugin(new FakeLockerPlugin());
}
+ // Allow view-only plugin for webdav requests
+ $this->server->addPlugin(new ViewOnlyPlugin(
+ $logger
+ ));
+
if (BrowserErrorPagePlugin::isBrowserRequest($request)) {
$this->server->addPlugin(new BrowserErrorPagePlugin());
}
diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
index f2d2014d553..73d21746b64 100644
--- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
+++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
@@ -27,7 +27,6 @@
*/
namespace OCA\DAV\Tests\unit\CalDAV;
-use OC\KnownUser\KnownUserService;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\Proxy\ProxyMapper;
use OCA\DAV\Connector\Sabre\Principal;
@@ -41,6 +40,8 @@ use OCP\IUserSession;
use OCP\L10N\IFactory;
use OCP\Security\ISecureRandom;
use OCP\Share\IManager as ShareManager;
+use OC\KnownUser\KnownUserService;
+use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV\Xml\Property\Href;
@@ -58,15 +59,18 @@ abstract class AbstractCalDavBackend extends TestCase {
/** @var CalDavBackend */
protected $backend;
- /** @var Principal | \PHPUnit\Framework\MockObject\MockObject */
+ /** @var Principal | MockObject */
protected $principal;
- /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var IUserManager|MockObject */
protected $userManager;
- /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var IGroupManager|MockObject */
protected $groupManager;
- /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var IEventDispatcher|MockObject */
protected $dispatcher;
+
+ /** @var IConfig | MockObject */
+ private $config;
/** @var ISecureRandom */
private $random;
/** @var LoggerInterface*/
diff --git a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
index 00fd0ebd8aa..3ac5b8f841a 100644
--- a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
@@ -29,8 +29,11 @@ namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OC\Files\FileInfo;
use OC\Files\View;
+use OC\Share20\ShareAttributes;
+use OCA\Files_Sharing\SharedStorage;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage;
+use OCP\Share\IAttributes;
use OCP\Share\IManager;
use OCP\Share\IShare;
@@ -169,6 +172,65 @@ class NodeTest extends \Test\TestCase {
$this->assertEquals($expected, $node->getSharePermissions($user));
}
+ public function testShareAttributes() {
+ $storage = $this->getMockBuilder(SharedStorage::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getShare'])
+ ->getMock();
+
+ $shareManager = $this->getMockBuilder(IManager::class)->disableOriginalConstructor()->getMock();
+ $share = $this->getMockBuilder(IShare::class)->disableOriginalConstructor()->getMock();
+
+ $storage->expects($this->once())
+ ->method('getShare')
+ ->willReturn($share);
+
+ $attributes = new ShareAttributes();
+ $attributes->setAttribute('permissions', 'download', false);
+
+ $share->expects($this->once())->method('getAttributes')->willReturn($attributes);
+
+ $info = $this->getMockBuilder(FileInfo::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getStorage', 'getType'])
+ ->getMock();
+
+ $info->method('getStorage')->willReturn($storage);
+ $info->method('getType')->willReturn(FileInfo::TYPE_FOLDER);
+
+ $view = $this->getMockBuilder(View::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $node = new \OCA\DAV\Connector\Sabre\File($view, $info);
+ $this->invokePrivate($node, 'shareManager', [$shareManager]);
+ $this->assertEquals($attributes->toArray(), $node->getShareAttributes());
+ }
+
+ public function testShareAttributesNonShare() {
+ $storage = $this->getMockBuilder(Storage::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $shareManager = $this->getMockBuilder(IManager::class)->disableOriginalConstructor()->getMock();
+
+ $info = $this->getMockBuilder(FileInfo::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getStorage', 'getType'])
+ ->getMock();
+
+ $info->method('getStorage')->willReturn($storage);
+ $info->method('getType')->willReturn(FileInfo::TYPE_FOLDER);
+
+ $view = $this->getMockBuilder(View::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $node = new \OCA\DAV\Connector\Sabre\File($view, $info);
+ $this->invokePrivate($node, 'shareManager', [$shareManager]);
+ $this->assertEquals([], $node->getShareAttributes());
+ }
+
public function sanitizeMtimeProvider() {
return [
[123456789, 123456789],
diff --git a/apps/dav/tests/unit/Controller/DirectControllerTest.php b/apps/dav/tests/unit/Controller/DirectControllerTest.php
index 00771e7f7a6..fe6d4ea8f24 100644
--- a/apps/dav/tests/unit/Controller/DirectControllerTest.php
+++ b/apps/dav/tests/unit/Controller/DirectControllerTest.php
@@ -34,11 +34,12 @@ use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\IRequest;
-use OCP\IURLGenerator;
+use OCP\IUrlGenerator;
use OCP\Security\ISecureRandom;
use Test\TestCase;
@@ -56,11 +57,13 @@ class DirectControllerTest extends TestCase {
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
private $timeFactory;
- /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var IUrlGenerator|\PHPUnit\Framework\MockObject\MockObject */
private $urlGenerator;
- /** @var DirectController */
- private $controller;
+ /** @var IEventDispatcher|\PHPUnit\Framework\MockObject\MockObject */
+ private $eventDispatcher;
+
+ private DirectController $controller;
protected function setUp(): void {
parent::setUp();
@@ -69,7 +72,8 @@ class DirectControllerTest extends TestCase {
$this->directMapper = $this->createMock(DirectMapper::class);
$this->random = $this->createMock(ISecureRandom::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->urlGenerator = $this->createMock(IUrlGenerator::class);
+ $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->controller = new DirectController(
'dav',
@@ -79,11 +83,12 @@ class DirectControllerTest extends TestCase {
$this->directMapper,
$this->random,
$this->timeFactory,
- $this->urlGenerator
+ $this->urlGenerator,
+ $this->eventDispatcher
);
}
- public function testGetUrlNonExistingFileId() {
+ public function testGetUrlNonExistingFileId(): void {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with('awesomeUser')
@@ -97,7 +102,7 @@ class DirectControllerTest extends TestCase {
$this->controller->getUrl(101);
}
- public function testGetUrlForFolder() {
+ public function testGetUrlForFolder(): void {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with('awesomeUser')
@@ -113,7 +118,7 @@ class DirectControllerTest extends TestCase {
$this->controller->getUrl(101);
}
- public function testGetUrlValid() {
+ public function testGetUrlValid(): void {
$userFolder = $this->createMock(Folder::class);
$this->rootFolder->method('getUserFolder')
->with('awesomeUser')
@@ -128,6 +133,9 @@ class DirectControllerTest extends TestCase {
->with(101)
->willReturn([$file]);
+ $userFolder->method('getRelativePath')
+ ->willReturn('/path');
+
$this->random->method('generate')
->with(
60,
diff --git a/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php b/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php
new file mode 100644
index 00000000000..f86a60fb4bf
--- /dev/null
+++ b/apps/dav/tests/unit/DAV/ViewOnlyPluginTest.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @author Piotr Mrowczynski piotr@owncloud.com
+ *
+ * @copyright Copyright (c) 2019, ownCloud GmbH
+ * @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\DAV\Tests\unit\DAV;
+
+use OCA\DAV\DAV\ViewOnlyPlugin;
+use OCA\Files_Sharing\SharedStorage;
+use OCA\DAV\Connector\Sabre\File as DavFile;
+use OCP\Files\File;
+use OCP\Files\Storage\IStorage;
+use OCP\Share\IAttributes;
+use OCP\Share\IShare;
+use Psr\Log\LoggerInterface;
+use Sabre\DAV\Server;
+use Sabre\DAV\Tree;
+use Test\TestCase;
+use Sabre\HTTP\RequestInterface;
+use OCA\DAV\Connector\Sabre\Exception\Forbidden;
+
+class ViewOnlyPluginTest extends TestCase {
+
+ private ViewOnlyPlugin $plugin;
+ /** @var Tree | \PHPUnit\Framework\MockObject\MockObject */
+ private $tree;
+ /** @var RequestInterface | \PHPUnit\Framework\MockObject\MockObject */
+ private $request;
+
+ public function setUp(): void {
+ $this->plugin = new ViewOnlyPlugin(
+ $this->createMock(LoggerInterface::class)
+ );
+ $this->request = $this->createMock(RequestInterface::class);
+ $this->tree = $this->createMock(Tree::class);
+
+ $server = $this->createMock(Server::class);
+ $server->tree = $this->tree;
+
+ $this->plugin->initialize($server);
+ }
+
+ public function testCanGetNonDav(): void {
+ $this->request->expects($this->once())->method('getPath')->willReturn('files/test/target');
+ $this->tree->method('getNodeForPath')->willReturn(null);
+
+ $this->assertTrue($this->plugin->checkViewOnly($this->request));
+ }
+
+ public function testCanGetNonShared(): void {
+ $this->request->expects($this->once())->method('getPath')->willReturn('files/test/target');
+ $davNode = $this->createMock(DavFile::class);
+ $this->tree->method('getNodeForPath')->willReturn($davNode);
+
+ $file = $this->createMock(File::class);
+ $davNode->method('getNode')->willReturn($file);
+
+ $storage = $this->createMock(IStorage::class);
+ $file->method('getStorage')->willReturn($storage);
+ $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(false);
+
+ $this->assertTrue($this->plugin->checkViewOnly($this->request));
+ }
+
+ public function providesDataForCanGet(): array {
+ return [
+ // has attribute permissions-download enabled - can get file
+ [ $this->createMock(File::class), true, true],
+ // has no attribute permissions-download - can get file
+ [ $this->createMock(File::class), null, true],
+ // has attribute permissions-download disabled- cannot get the file
+ [ $this->createMock(File::class), false, false],
+ ];
+ }
+
+ /**
+ * @dataProvider providesDataForCanGet
+ */
+ public function testCanGet(File $nodeInfo, ?bool $attrEnabled, bool $expectCanDownloadFile): void {
+ $this->request->expects($this->once())->method('getPath')->willReturn('files/test/target');
+
+ $davNode = $this->createMock(DavFile::class);
+ $this->tree->method('getNodeForPath')->willReturn($davNode);
+
+ $davNode->method('getNode')->willReturn($nodeInfo);
+
+ $storage = $this->createMock(SharedStorage::class);
+ $share = $this->createMock(IShare::class);
+ $nodeInfo->method('getStorage')->willReturn($storage);
+ $storage->method('instanceOfStorage')->with(SharedStorage::class)->willReturn(true);
+ $storage->method('getShare')->willReturn($share);
+
+ $extAttr = $this->createMock(IAttributes::class);
+ $share->method('getAttributes')->willReturn($extAttr);
+ $extAttr->method('getAttribute')->with('permissions', 'download')->willReturn($attrEnabled);
+
+ if (!$expectCanDownloadFile) {
+ $this->expectException(Forbidden::class);
+ }
+ $this->plugin->checkViewOnly($this->request);
+ }
+}