77
88namespace OCA \Activity \Controller ;
99
10+ use DateTimeInterface ;
11+ use OC \Files \Storage \Wrapper \Wrapper ;
1012use OCA \Activity \Extension \Files ;
13+ use OCA \Files_Sharing \External \Storage as ExternalStorage ;
1114use OCP \Activity \IManager as IActivityManager ;
1215use OCP \App \IAppManager ;
1316use OCP \AppFramework \Http ;
17+ use OCP \AppFramework \Http \Attribute \BruteForceProtection ;
1418use OCP \AppFramework \Http \DataResponse ;
1519use OCP \AppFramework \OCSController ;
20+ use OCP \AppFramework \Utility \ITimeFactory ;
21+ use OCP \Federation \ICloudIdManager ;
1622use OCP \Files \InvalidPathException ;
1723use OCP \Files \IRootFolder ;
1824use OCP \Files \NotFoundException ;
@@ -30,6 +36,8 @@ public function __construct(
3036 protected IAppManager $ appManager ,
3137 protected IRootFolder $ rootFolder ,
3238 protected IActivityManager $ activityManager ,
39+ protected ICloudIdManager $ cloudIdManager ,
40+ protected ITimeFactory $ timeFactory ,
3341 ) {
3442 parent ::__construct ($ appName , $ request );
3543 }
@@ -48,20 +56,26 @@ public function __construct(
4856 * @param string[] $origin
4957 * @return DataResponse
5058 */
51- public function receiveActivity ($ token , array $ to , array $ actor , $ type , $ updated , array $ object = [], array $ target = [], array $ origin = []) {
52- $ date = \DateTime::createFromFormat (\DateTime::W3C , $ updated );
53- if ($ date === false ) {
59+ #[BruteForceProtection(action: 'receiveActivity ' )]
60+ public function receiveActivity ($ token , array $ to , array $ actor , $ type , $ updated , array $ object = [], array $ target = [], array $ origin = []): DataResponse {
61+ if (!$ this ->appManager ->isInstalled ('federatedfilesharing ' )) {
62+ return new DataResponse ([], Http::STATUS_NOT_FOUND );
63+ }
64+
65+ $ date = \DateTime::createFromFormat (DateTimeInterface::W3C , $ updated );
66+ if ($ date === false || abs ($ date ->getTimestamp () - $ this ->timeFactory ->getTime ()) > 600 ) {
5467 return new DataResponse ([], Http::STATUS_BAD_REQUEST );
5568 }
56- $ time = $ date ->getTimestamp ();
5769
5870 if (!isset ($ to ['type ' ], $ to ['name ' ]) || $ to ['type ' ] !== 'Person ' ) {
5971 return new DataResponse ([], Http::STATUS_BAD_REQUEST );
6072 }
6173
6274 $ user = $ this ->userManager ->get ($ to ['name ' ]);
6375 if (!$ user instanceof IUser) {
64- return new DataResponse ([], Http::STATUS_NOT_FOUND );
76+ $ response = new DataResponse ([], Http::STATUS_NOT_FOUND );
77+ $ response ->throttle ();
78+ return $ response ;
6579 }
6680
6781 if (!isset ($ actor ['type ' ], $ actor ['name ' ]) || $ actor ['type ' ] !== 'Person ' ) {
@@ -72,27 +86,44 @@ public function receiveActivity($token, array $to, array $actor, $type, $updated
7286 return new DataResponse ([], Http::STATUS_BAD_REQUEST );
7387 }
7488
75- if (!$ this ->appManager ->isInstalled ('federatedfilesharing ' )) {
76- return new DataResponse ([], Http::STATUS_NOT_FOUND );
89+ if (isset ($ object ['name ' ]) && preg_match ('/(^|\/)\.\.(\/|$)/ ' , $ object ['name ' ])) {
90+ return new DataResponse ([], Http::STATUS_BAD_REQUEST );
91+ }
92+
93+ try {
94+ $ resolved = $ this ->cloudIdManager ->resolveCloudId ($ actor ['name ' ]);
95+ $ actorServer = $ resolved ->getRemote ();
96+ $ actorUser = $ resolved ->getUser ();
97+ } catch (\InvalidArgumentException ) {
98+ return new DataResponse ([], Http::STATUS_BAD_REQUEST );
99+ }
100+
101+ $ internalType = $ this ->translateType ($ type );
102+ if ($ internalType === '' ) {
103+ return new DataResponse ([], Http::STATUS_BAD_REQUEST );
77104 }
78105
79106 $ query = $ this ->db ->getQueryBuilder ();
80107 $ query ->select ('* ' )
81108 ->from ('share_external ' )
82109 ->where ($ query ->expr ()->eq ('share_token ' , $ query ->createNamedParameter ($ token )))
83- ->andWhere ($ query ->expr ()->eq ('user ' , $ query ->createNamedParameter ($ user ->getUID ())));
110+ ->andWhere ($ query ->expr ()->eq ('user ' , $ query ->createNamedParameter ($ user ->getUID ())))
111+ ->andWhere ($ query ->expr ()->eq ('owner ' , $ query ->createNamedParameter ($ actorUser )));
84112
85113 $ result = $ query ->executeQuery ();
86114 $ share = $ result ->fetch ();
87115 $ result ->closeCursor ();
88116
89- if (!is_array ($ share ) || strpos ($ share ['mountpoint ' ], '{{TemporaryMountPointName# ' ) === 0 ) {
90- return new DataResponse ([], Http::STATUS_NOT_FOUND );
117+ if (!is_array ($ share ) || str_starts_with ($ share ['mountpoint ' ], '{{TemporaryMountPointName# ' )) {
118+ $ response = new DataResponse ([], Http::STATUS_NOT_FOUND );
119+ $ response ->throttle ();
120+ return $ response ;
91121 }
92122
93- $ internalType = $ this ->translateType ($ type );
94- if ($ internalType === '' ) {
95- return new DataResponse ([], Http::STATUS_BAD_REQUEST );
123+ $ normalizedActorServer = rtrim (strtolower (preg_replace ('/^https?:\/\// ' , '' , $ actorServer )), '/ ' );
124+ $ normalizedShareRemote = rtrim (strtolower (preg_replace ('/^https?:\/\// ' , '' , $ share ['remote ' ])), '/ ' );
125+ if ($ normalizedActorServer !== $ normalizedShareRemote ) {
126+ return new DataResponse ([], Http::STATUS_FORBIDDEN );
96127 }
97128
98129 $ path2 = null ;
@@ -111,7 +142,6 @@ public function receiveActivity($token, array $to, array $actor, $type, $updated
111142 if (!isset ($ object ['type ' ], $ object ['name ' ]) || $ object ['type ' ] !== 'Document ' ) {
112143 return new DataResponse ([], Http::STATUS_BAD_REQUEST );
113144 }
114-
115145 $ path = $ share ['mountpoint ' ] . $ object ['name ' ];
116146 }
117147
@@ -124,10 +154,25 @@ public function receiveActivity($token, array $to, array $actor, $type, $updated
124154 try {
125155 $ node = $ userFolder ->get ($ path );
126156 $ fileId = $ node ->getId ();
127- } catch (NotFoundException $ e ) {
128- return new DataResponse ([], Http::STATUS_NOT_FOUND );
129- } catch (InvalidPathException $ e ) {
130- return new DataResponse ([], Http::STATUS_NOT_FOUND );
157+
158+ $ storage = $ node ->getStorage ();
159+ if (!$ storage ->instanceOfStorage (ExternalStorage::class)) {
160+ $ response = new DataResponse ([], Http::STATUS_FORBIDDEN );
161+ $ response ->throttle ();
162+ return $ response ;
163+ }
164+ while ($ storage instanceof Wrapper) {
165+ $ storage = $ storage ->getWrapperStorage ();
166+ }
167+ if (!($ storage instanceof ExternalStorage) || $ storage ->getToken () !== $ token ) {
168+ $ response = new DataResponse ([], Http::STATUS_FORBIDDEN );
169+ $ response ->throttle ();
170+ return $ response ;
171+ }
172+ } catch (NotFoundException |InvalidPathException ) {
173+ $ response = new DataResponse ([], Http::STATUS_NOT_FOUND );
174+ $ response ->throttle ();
175+ return $ response ;
131176 }
132177
133178 if ($ path2 !== null ) {
@@ -136,8 +181,7 @@ public function receiveActivity($token, array $to, array $actor, $type, $updated
136181 try {
137182 $ parent = $ node ->getParent ();
138183 $ secondPath = [$ parent ->getId () => dirname ($ path2 )];
139- } catch (NotFoundException $ e ) {
140- } catch (InvalidPathException $ e ) {
184+ } catch (NotFoundException |InvalidPathException ) {
141185 }
142186 }
143187 $ subjectParams = [$ secondPath , $ actor ['name ' ], [$ fileId => $ path ]];
@@ -153,11 +197,11 @@ public function receiveActivity($token, array $to, array $actor, $type, $updated
153197 ->setAuthor ($ actor ['name ' ])
154198 ->setObject ('files ' , $ fileId , $ path )
155199 ->setSubject ($ subject , $ subjectParams )
156- ->setTimestamp ($ time );
200+ ->setTimestamp ($ date -> getTimestamp () );
157201 $ this ->activityManager ->publish ($ event );
158- } catch (\InvalidArgumentException $ e ) {
202+ } catch (\InvalidArgumentException ) {
159203 return new DataResponse (['activity ' ], Http::STATUS_BAD_REQUEST );
160- } catch (\BadMethodCallException $ e ) {
204+ } catch (\BadMethodCallException ) {
161205 return new DataResponse (['sending ' ], Http::STATUS_BAD_REQUEST );
162206 }
163207
0 commit comments