-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2300 from nextcloud/feat/neon_http_client/csrf_in…
…terceptor feat(neon_http_client): add csrf interceptor
- Loading branch information
Showing
17 changed files
with
454 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
platforms: | ||
- vm | ||
- chrome | ||
|
||
define_platforms: | ||
chromium: | ||
name: Chromium | ||
extends: chrome | ||
settings: | ||
executable: chromium |
99 changes: 99 additions & 0 deletions
99
packages/neon_http_client/lib/src/interceptors/csrf_interceptor.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import 'package:http/http.dart' as http; | ||
import 'package:logging/logging.dart'; | ||
import 'package:meta/meta.dart'; | ||
import 'package:neon_http_client/src/interceptors/http_interceptor.dart'; | ||
import 'package:nextcloud/nextcloud.dart'; | ||
import 'package:nextcloud/webdav.dart'; | ||
import 'package:universal_io/io.dart'; | ||
|
||
/// A HttpInterceptor that works around a Nextcloud CSRF bug when cookies are sent. | ||
/// | ||
/// A fix is proposed in: https://github.com/nextcloud/server/pull/43699 | ||
/// | ||
/// | ||
/// Because cookies are always sent when run on web this interceptor will acquire | ||
/// a token from the server and attach it to the request headers. | ||
/// If the response is has a `401` status code the token is cleared so a future | ||
/// request will acquire a new one. | ||
/// | ||
/// On non web platforms the interceptor will simply remove the `cookies` header | ||
/// for requests to the webdav endpoint. | ||
@internal | ||
final class CSRFInterceptor implements HttpInterceptor { | ||
/// Creates a new csrf interceptor. | ||
CSRFInterceptor({ | ||
required http.Client client, | ||
required Uri baseURL, | ||
}) : _client = client, | ||
_baseURL = baseURL; | ||
|
||
final http.Client _client; | ||
|
||
final Uri _baseURL; | ||
|
||
/// The request token sent by the [CSRFInterceptor]. | ||
@visibleForTesting | ||
String? token; | ||
|
||
static final _log = Logger('CSRFInterceptor'); | ||
|
||
// ignore: do_not_use_environment | ||
static const bool _kIsWeb = bool.fromEnvironment('dart.library.js_util'); | ||
|
||
@override | ||
bool shouldInterceptRequest(http.BaseRequest request) { | ||
if (request.url.host != _baseURL.host || !request.url.path.startsWith('${_baseURL.path}$webdavBase')) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
@override | ||
Future<http.BaseRequest> interceptRequest({required http.BaseRequest request}) async { | ||
assert( | ||
shouldInterceptRequest(request), | ||
'Request should not be intercepted.', | ||
); | ||
|
||
if (!_kIsWeb) { | ||
return request..headers.remove(HttpHeaders.cookieHeader); | ||
} | ||
|
||
if (token == null) { | ||
_log.fine('Acquiring new CSRF token for WebDAV'); | ||
|
||
final response = await _client.get(Uri.parse('$_baseURL/index.php')); | ||
if (response.statusCode >= 300) { | ||
throw DynamiteStatusCodeException(response); | ||
} | ||
|
||
token = RegExp('data-requesttoken="([^"]*)"').firstMatch(response.body)!.group(1); | ||
} | ||
|
||
request.headers.addAll({ | ||
'OCS-APIRequest': 'true', | ||
'requesttoken': token!, | ||
}); | ||
|
||
return request; | ||
} | ||
|
||
@override | ||
bool shouldInterceptResponse(http.StreamedResponse response) { | ||
return _kIsWeb && response.statusCode == 401; | ||
} | ||
|
||
@override | ||
http.StreamedResponse interceptResponse({required http.StreamedResponse response, required Uri url}) { | ||
assert( | ||
shouldInterceptResponse(response), | ||
'Response should not be intercepted.', | ||
); | ||
|
||
// Clear the token just in case it expired and lead to the failure. | ||
_log.fine('Clearing CSRF token for WebDAV'); | ||
token = null; | ||
return response; | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
packages/neon_http_client/lib/src/interceptors/interceptors.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export 'base_header_interceptor.dart'; | ||
export 'cookie_interceptor.dart'; | ||
export 'csrf_interceptor.dart'; | ||
export 'http_interceptor.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
# melos_managed_dependency_overrides: cookie_store,neon_lints | ||
# melos_managed_dependency_overrides: cookie_store,dynamite_runtime,neon_lints,nextcloud | ||
dependency_overrides: | ||
cookie_store: | ||
path: ../cookie_store | ||
dynamite_runtime: | ||
path: ../dynamite/dynamite_runtime | ||
neon_lints: | ||
path: ../neon_lints | ||
nextcloud: | ||
path: ../nextcloud |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.