From 2d97059a0696b99c018e36e701fd08cd2f91b53b Mon Sep 17 00:00:00 2001 From: xuty <xty50337@hotmail.com> Date: Wed, 18 Aug 2021 14:14:11 +0000 Subject: [PATCH] Fixes signing error #29 --- CHANGELOG.md | 5 +++++ lib/src/minio_helpers.dart | 39 ++++++++++++++++++++++++++++++++++++++ lib/src/minio_sign.dart | 2 +- test/minio_test.dart | 11 ++++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f18e1..5b18d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 3.0.0 +- Fixes signing error in case object name contains symbols [#29] + # 2.1.0-pre - `getObject` now returns `MinioByteStream` with an additional `contentLength` field. @@ -77,3 +80,5 @@ ## 0.1.0 - Initial version, created by Stagehand + +[#29]: https://github.com/xtyxtyx/minio-dart/issues/29 \ No newline at end of file diff --git a/lib/src/minio_helpers.dart b/lib/src/minio_helpers.dart index 0eeebe4..bebc4ff 100644 --- a/lib/src/minio_helpers.dart +++ b/lib/src/minio_helpers.dart @@ -1,3 +1,4 @@ +import 'package:convert/convert.dart'; import 'package:http/http.dart'; import 'package:mime/mime.dart' show lookupMimeType; import 'package:minio/src/minio_errors.dart'; @@ -231,3 +232,41 @@ void validate(Response response, {int? expect}) { '$expect expected, got ${response.statusCode}', null, response); } } + +final _a = 'a'.codeUnitAt(0); +final _A = 'A'.codeUnitAt(0); +final _z = 'z'.codeUnitAt(0); +final _Z = 'Z'.codeUnitAt(0); +final _0 = '0'.codeUnitAt(0); +final _9 = '9'.codeUnitAt(0); + +final _pathIgnoredChars = { + '%'.codeUnitAt(0), + '-'.codeUnitAt(0), + '_'.codeUnitAt(0), + '.'.codeUnitAt(0), + '~'.codeUnitAt(0), + '/'.codeUnitAt(0), +}; + +/// encode [uri].path to HTML hex escape sequence +String encodePath(Uri uri) { + final result = StringBuffer(); + for (var char in uri.path.codeUnits) { + if (_A <= char && char <= _Z || + _a <= char && char <= _z || + _0 <= char && char <= _9) { + result.writeCharCode(char); + continue; + } + + if (_pathIgnoredChars.contains(char)) { + result.writeCharCode(char); + continue; + } + + result.write('%'); + result.write(hex.encode([char]).toUpperCase()); + } + return result.toString(); +} diff --git a/lib/src/minio_sign.dart b/lib/src/minio_sign.dart index a1fc9fe..9f62262 100644 --- a/lib/src/minio_sign.dart +++ b/lib/src/minio_sign.dart @@ -44,7 +44,7 @@ String getCanonicalRequest( List<String> signedHeaders, String hashedPayload, ) { - final requestResource = request.url.path; + final requestResource = encodePath(request.url); final headers = signedHeaders.map( (header) => '${header.toLowerCase()}:${request.headers[header]}', ); diff --git a/test/minio_test.dart b/test/minio_test.dart index 02ab20b..3f00c45 100644 --- a/test/minio_test.dart +++ b/test/minio_test.dart @@ -362,7 +362,6 @@ void testPutObject() { group('putObject()', () { final minio = getMinioClient(); final bucketName = DateTime.now().millisecondsSinceEpoch.toString(); - final objectName = DateTime.now().microsecondsSinceEpoch.toString(); final objectData = Uint8List.fromList([1, 2, 3]); setUpAll(() async { @@ -374,6 +373,16 @@ void testPutObject() { }); test('succeeds', () async { + final objectName = DateTime.now().microsecondsSinceEpoch.toString(); + await minio.putObject(bucketName, objectName, Stream.value(objectData)); + final stat = await minio.statObject(bucketName, objectName); + expect(stat.size, equals(objectData.length)); + await minio.removeObject(bucketName, objectName); + }); + + test('works with object names with symbols', () async { + final objectName = + DateTime.now().microsecondsSinceEpoch.toString() + r'-._~,!@#$%^&*()'; await minio.putObject(bucketName, objectName, Stream.value(objectData)); final stat = await minio.statObject(bucketName, objectName); expect(stat.size, equals(objectData.length)); -- GitLab