1515use OCA \Recognize \Db \FaceDetectionWithTitle ;
1616use OCP \AppFramework \Db \DoesNotExistException ;
1717use OCP \AppFramework \Db \MultipleObjectsReturnedException ;
18+ use OCP \AppFramework \Utility \ITimeFactory ;
1819use OCP \DB \Exception ;
1920use OCP \Files \DavUtil ;
2021use OCP \IPreview ;
22+ use OCP \Security \ICrypto ;
23+ use Psr \Log \LoggerInterface ;
2124use Sabre \DAV \Exception \Forbidden ;
2225use Sabre \DAV \INode ;
2326use Sabre \DAV \PropFind ;
2427use Sabre \DAV \Server ;
2528use Sabre \DAV \ServerPlugin ;
29+ use Sabre \HTTP \RequestInterface ;
30+ use Sabre \HTTP \ResponseInterface ;
2631
2732final class PropFindPlugin extends ServerPlugin {
2833 public const FACE_DETECTIONS_PROPERTYNAME = '{http://nextcloud.org/ns}face-detections ' ;
@@ -31,12 +36,17 @@ final class PropFindPlugin extends ServerPlugin {
3136 public const NBITEMS_PROPERTYNAME = '{http://nextcloud.org/ns}nbItems ' ;
3237 public const FACE_PREVIEW_IMAGE_PROPERTYNAME = '{http://nextcloud.org/ns}face-preview-image ' ;
3338
39+ public const API_KEY_TIMEOUT = 60 * 60 * 24 ;
40+
3441 private Server $ server ;
3542
3643 public function __construct (
3744 private FaceDetectionMapper $ faceDetectionMapper ,
3845 private IPreview $ previewManager ,
3946 private FaceClusterMapper $ faceClusterMapper ,
47+ private ICrypto $ crypto ,
48+ private LoggerInterface $ logger ,
49+ private ITimeFactory $ timeFactory ,
4050 ) {
4151 }
4252
@@ -45,6 +55,7 @@ public function initialize(Server $server) {
4555
4656 $ this ->server ->on ('propFind ' , [$ this , 'propFind ' ]);
4757 $ this ->server ->on ('beforeMove ' , [$ this , 'beforeMove ' ]);
58+ $ this ->server ->on ('beforeMethod:* ' , [$ this , 'beforeMethod ' ], 1 );
4859 }
4960
5061
@@ -113,4 +124,38 @@ public function beforeMove($source, $target) {
113124 }
114125 return true ;
115126 }
127+
128+ public function beforeMethod (RequestInterface $ request , ResponseInterface $ response ) {
129+ if (!str_starts_with ($ request ->getPath (), 'recognize ' )) {
130+ return ;
131+ }
132+ $ key = $ request ->getHeader ('X-Recognize-Api-Key ' );
133+ if ($ key === null ) {
134+ throw new Forbidden ('You must provide a valid X-Recognize-Api-Key ' );
135+ }
136+ try {
137+ $ json = $ this ->crypto ->decrypt ($ key );
138+ } catch (\Exception $ e ) {
139+ $ this ->logger ->warning ('Failed to decrypt recognize API key. Denying entry. ' , ['exception ' => $ e ]);
140+ throw new Forbidden ('You must provide a valid X-Recognize-Api-Key ' );
141+ }
142+ try {
143+ $ data = json_decode ($ json , true , 512 , JSON_THROW_ON_ERROR );
144+ } catch (\JsonException $ e ) {
145+ $ this ->logger ->warning ('Failed to decode recognize API key. Denying entry. ' , ['exception ' => $ e ]);
146+ throw new Forbidden ('You must provide a valid X-Recognize-Api-Key ' );
147+ }
148+
149+ if (!isset ($ data ['type ' ]) || $ data ['type ' ] !== 'recognize-api-key ' || !isset ($ data ['version ' ]) || $ data ['version ' ] !== 1 || !isset ($ data ['timestamp ' ])) {
150+ $ this ->logger ->warning ('Failed to validate recognize API key. ' , ['data ' => $ data ]);
151+ throw new Forbidden ('You must provide a valid X-Recognize-Api-Key ' );
152+ }
153+
154+ if ($ this ->timeFactory ->now ()->getTimestamp () - (int )$ data ['timestamp ' ] < self ::API_KEY_TIMEOUT ) {
155+ return ;
156+ }
157+
158+ $ this ->logger ->info ('API key is too old, denying entry ' , ['data ' => $ data ]);
159+ throw new Forbidden ('You must provide a valid X-Recognize-Api-Key ' );
160+ }
116161}
0 commit comments