diff options
author | 杨宇千 <crupest@outlook.com> | 2019-07-29 21:13:01 +0800 |
---|---|---|
committer | 杨宇千 <crupest@outlook.com> | 2019-07-29 21:13:01 +0800 |
commit | 34c7f2ad8e3227454003e3ebfdf4766cb1014dd2 (patch) | |
tree | 1c036752a2552f1a536f87ff72e27fb23e04206a | |
parent | e21ef1ac89c799ded14fce8f4f319b0e5ac7ab81 (diff) | |
download | timeline-34c7f2ad8e3227454003e3ebfdf4766cb1014dd2.tar.gz timeline-34c7f2ad8e3227454003e3ebfdf4766cb1014dd2.tar.bz2 timeline-34c7f2ad8e3227454003e3ebfdf4766cb1014dd2.zip |
Remove QCloud COS completely.
-rw-r--r-- | Timeline.Tests/QCloudCosServiceUnitTest.cs | 100 | ||||
-rw-r--r-- | Timeline/Configs/QCloudCosConfig.cs | 10 | ||||
-rw-r--r-- | Timeline/Services/QCloudCosService.cs | 328 |
3 files changed, 0 insertions, 438 deletions
diff --git a/Timeline.Tests/QCloudCosServiceUnitTest.cs b/Timeline.Tests/QCloudCosServiceUnitTest.cs deleted file mode 100644 index c330cce0..00000000 --- a/Timeline.Tests/QCloudCosServiceUnitTest.cs +++ /dev/null @@ -1,100 +0,0 @@ -using Microsoft.AspNetCore.Mvc.Testing; -using System; -using System.Collections.Generic; -using Timeline.Services; -using Timeline.Tests.Helpers; -using Xunit; -using Xunit.Abstractions; - -namespace Timeline.Tests -{ - public class QCloudCosServiceUnitTest : IClassFixture<WebApplicationFactory<Startup>> - { - private readonly WebApplicationFactory<Startup> _factory; - - public QCloudCosServiceUnitTest(WebApplicationFactory<Startup> factory, ITestOutputHelper outputHelper) - { - _factory = factory.WithTestConfig(outputHelper); - _factory.CreateDefaultClient().Dispose(); // Ensure test server is created. - } - - [Fact] - public void ValidateBucketNameTest() - { - Assert.True(QCloudCosService.ValidateBucketName("hello")); - Assert.True(QCloudCosService.ValidateBucketName("hello0123")); - Assert.True(QCloudCosService.ValidateBucketName("hello0123-hello")); - Assert.False(QCloudCosService.ValidateBucketName("-hello")); - Assert.False(QCloudCosService.ValidateBucketName("hello-")); - Assert.False(QCloudCosService.ValidateBucketName("helloU")); - Assert.False(QCloudCosService.ValidateBucketName("hello!")); - } - - [Fact] - public void GenerateSignatureTest() - { - var credential = new QCloudCosService.QCloudCredentials - { - SecretId = "AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q", - SecretKey = "BQYIM75p8x0iWVFSIgqEKwFprpRSVHlz" - }; - - var request = new QCloudCosService.RequestInfo - { - Method = "put", - Uri = "/exampleobject", - Parameters = new Dictionary<string, string>(), - Headers = new Dictionary<string, string> - { - ["Host"] = "examplebucket-1250000000.cos.ap-beijing.myqcloud.com", - ["x-cos-storage-class"] = "standard", - ["x-cos-content-sha1"] = "b502c3a1f48c8609ae212cdfb639dee39673f5e" - } - }; - - var signValidTime = new QCloudCosService.TimeDuration - { - Start = DateTimeOffset.FromUnixTimeSeconds(1417773892), - End = DateTimeOffset.FromUnixTimeSeconds(1417853898) - }; - - Assert.Equal("q-sign-algorithm=sha1&q-ak=AKIDQjz3ltompVjBni5LitkWHFlFpwkn9U5q&q-sign-time=1417773892;1417853898&q-key-time=1417773892;1417853898&q-header-list=host;x-cos-content-sha1;x-cos-storage-class&q-url-param-list=&q-signature=0ab12f43e74cbe148d705cd9fae8adc9a6d39cc1", QCloudCosService.GenerateSign(credential, request, signValidTime)); - } - - /* - // Tests in this part need secret configs in cos. - #region SecretTests - [Fact] - public async Task ObjectExistsTest() - { - using (var serviceScope = _factory.Server.Host.Services.CreateScope()) - { - var services = serviceScope.ServiceProvider; - var service = services.GetRequiredService<IQCloudCosService>(); - Assert.True(await service.IsObjectExists("avatar", "__default")); - Assert.False(await service.IsObjectExists("avatar", "haha")); - Assert.False(await service.IsObjectExists("haha", "haha")); - } - } - - [Fact] - public async Task GenerateObjectGetUrlTest() - { - using (var serviceScope = _factory.Server.Host.Services.CreateScope()) - { - var services = serviceScope.ServiceProvider; - var service = services.GetRequiredService<IQCloudCosService>(); - var url = service.GenerateObjectGetUrl("avatar", "__default"); - // never use the following line! Because client created by factory can't access Internet. - //using (var client = _factory.CreateClient()) - using (var client = services.GetRequiredService<IHttpClientFactory>().CreateClient()) - { - var res = await client.GetAsync(url); - Assert.Equal(HttpStatusCode.OK, res.StatusCode); - } - } - } - #endregion - */ - } -} diff --git a/Timeline/Configs/QCloudCosConfig.cs b/Timeline/Configs/QCloudCosConfig.cs deleted file mode 100644 index 6d10436c..00000000 --- a/Timeline/Configs/QCloudCosConfig.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Timeline.Configs -{ - public class QCloudCosConfig - { - public string AppId { get; set; } - public string Region { get; set; } - public string SecretId { get; set; } - public string SecretKey { get; set; } - } -} diff --git a/Timeline/Services/QCloudCosService.cs b/Timeline/Services/QCloudCosService.cs deleted file mode 100644 index 748173c4..00000000 --- a/Timeline/Services/QCloudCosService.cs +++ /dev/null @@ -1,328 +0,0 @@ -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using Timeline.Configs; - -namespace Timeline.Services -{ - public interface IQCloudCosService - { - /// <summary> - /// Test if an object in the bucket exists. - /// </summary> - /// <param name="bucket">The bucket name.</param> - /// <param name="key">The object key.</param> - /// <returns>True if exists. False if not.</returns> - Task<bool> IsObjectExists(string bucket, string key); - - /// <summary> - /// Upload an object use put method. - /// </summary> - /// <param name="bucket">The bucket name.</param> - /// <param name="key">The object key.</param> - /// <param name="data">The data to upload.</param> - Task PutObject(string bucket, string key, byte[] data, string contentType); - - /// <summary> - /// Generate a presignated url to access the object. - /// </summary> - /// <param name="bucket">The bucket name.</param> - /// <param name="key">The object key.</param> - /// <returns>The presignated url.</returns> - string GenerateObjectGetUrl(string bucket, string key); - } - - public class QCloudCosService : IQCloudCosService - { - private readonly IOptionsMonitor<QCloudCosConfig> _config; - private readonly ILogger<QCloudCosService> _logger; - private readonly IHttpClientFactory _httpClientFactory; - - public QCloudCosService(IOptionsMonitor<QCloudCosConfig> config, ILogger<QCloudCosService> logger, IHttpClientFactory httpClientFactory) - { - _config = config; - _logger = logger; - _httpClientFactory = httpClientFactory; - } - - private const string BucketNamePattern = @"^(([a-z0-9][a-z0-9-]*[a-z0-9])|[a-z0-9])$"; - - public static bool ValidateBucketName(string bucketName) - { - return Regex.IsMatch(bucketName, BucketNamePattern); - } - - public class QCloudCredentials - { - public string SecretId { get; set; } - public string SecretKey { get; set; } - } - - public class RequestInfo - { - public string Method { get; set; } - public string Uri { get; set; } - public IEnumerable<KeyValuePair<string, string>> Parameters { get; set; } - public IEnumerable<KeyValuePair<string, string>> Headers { get; set; } - } - - public class TimeDuration - { - public TimeDuration() - { - - } - - public TimeDuration(DateTimeOffset start, DateTimeOffset end) - { - Start = start; - End = end; - } - - public DateTimeOffset Start { get; set; } - public DateTimeOffset End { get; set; } - } - - public static string GenerateSign(QCloudCredentials credentials, RequestInfo request, TimeDuration signValidTime) - { - Debug.Assert(credentials != null); - Debug.Assert(credentials.SecretId != null); - Debug.Assert(credentials.SecretKey != null); - Debug.Assert(request != null); - Debug.Assert(request.Method != null); - Debug.Assert(request.Uri != null); - Debug.Assert(signValidTime != null); - Debug.Assert(signValidTime.Start < signValidTime.End, "Start must be before End in sign valid time."); - - List<(string key, string value)> Transform(IEnumerable<KeyValuePair<string, string>> raw) - { - if (raw == null) - return new List<(string key, string value)>(); - - var sorted = raw.Select(p => (key: p.Key.ToLower(), value: WebUtility.UrlEncode(p.Value))).ToList(); - sorted.Sort((left, right) => string.CompareOrdinal(left.key, right.key)); - return sorted; - } - - var transformedParameters = Transform(request.Parameters); - var transformedHeaders = Transform(request.Headers); - - List<(string, string)> result = new List<(string, string)>(); - - const string signAlgorithm = "sha1"; - result.Add(("q-sign-algorithm", signAlgorithm)); - - result.Add(("q-ak", credentials.SecretId)); - - var signTime = $"{signValidTime.Start.ToUnixTimeSeconds().ToString()};{signValidTime.End.ToUnixTimeSeconds().ToString()}"; - var keyTime = signTime; - result.Add(("q-sign-time", signTime)); - result.Add(("q-key-time", keyTime)); - - result.Add(("q-header-list", string.Join(';', transformedHeaders.Select(h => h.key)))); - result.Add(("q-url-param-list", string.Join(';', transformedParameters.Select(p => p.key)))); - - HMACSHA1 hmac = new HMACSHA1(); - - string ByteArrayToString(byte[] bytes) - { - return BitConverter.ToString(bytes).Replace("-", "").ToLower(); - } - - hmac.Key = Encoding.UTF8.GetBytes(credentials.SecretKey); - var signKey = ByteArrayToString(hmac.ComputeHash(Encoding.UTF8.GetBytes(keyTime))); - - string Join(IEnumerable<(string key, string value)> raw) - { - return string.Join('&', raw.Select(p => string.Concat(p.key, "=", p.value))); - } - - var httpString = new StringBuilder() - .Append(request.Method.ToLower()).Append('\n') - .Append(request.Uri).Append('\n') - .Append(Join(transformedParameters)).Append('\n') - .Append(Join(transformedHeaders)).Append('\n') - .ToString(); - - string Sha1(string data) - { - var sha1 = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(data)); - return ByteArrayToString(sha1); - } - - var stringToSign = new StringBuilder() - .Append(signAlgorithm).Append('\n') - .Append(signTime).Append('\n') - .Append(Sha1(httpString)).Append('\n') - .ToString(); - - hmac.Key = Encoding.UTF8.GetBytes(signKey); - var signature = ByteArrayToString(hmac.ComputeHash( - Encoding.UTF8.GetBytes(stringToSign))); - - result.Add(("q-signature", signature)); - - return Join(result); - } - - private QCloudCredentials GetCredentials() - { - var config = _config.CurrentValue; - return new QCloudCredentials - { - SecretId = config.SecretId, - SecretKey = config.SecretKey - }; - } - - private string GetHost(string bucket) - { - var config = _config.CurrentValue; - return $"{bucket}-{config.AppId}.cos.{config.Region}.myqcloud.com"; - } - - public async Task<bool> IsObjectExists(string bucket, string key) - { - if (bucket == null) - throw new ArgumentNullException(nameof(bucket)); - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (!ValidateBucketName(bucket)) - throw new ArgumentException($"Bucket name is not valid. Param is {bucket} .", nameof(bucket)); - - var client = _httpClientFactory.CreateClient(); - - var host = GetHost(bucket); - var encodedKey = WebUtility.UrlEncode(key); - - var request = new HttpRequestMessage(); - request.Method = HttpMethod.Head; - request.RequestUri = new Uri($"https://{host}/{encodedKey}"); - request.Headers.Host = host; - request.Headers.Date = DateTimeOffset.Now; - request.Headers.TryAddWithoutValidation("Authorization", GenerateSign(GetCredentials(), new RequestInfo - { - Method = "head", - Uri = "/" + encodedKey, - Headers = new Dictionary<string, string> - { - ["Host"] = host - } - }, new TimeDuration(DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(2)))); - - try - { - var response = await client.SendAsync(request); - - if (response.IsSuccessStatusCode) - return true; - if (response.StatusCode == HttpStatusCode.NotFound) - return false; - - throw new Exception($"Unknown response code. {response.ToString()}"); - } - catch (Exception e) - { - _logger.LogError(e, "An error occured when test a cos object existence."); - throw; - } - } - - public async Task PutObject(string bucket, string key, byte[] data, string contentType) - { - if (bucket == null) - throw new ArgumentNullException(nameof(bucket)); - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (!ValidateBucketName(bucket)) - throw new ArgumentException($"Bucket name is not valid. Param is {bucket} .", nameof(bucket)); - if (data == null) - throw new ArgumentNullException(nameof(data)); - - var host = GetHost(bucket); - var encodedKey = WebUtility.UrlEncode(key); - var md5 = Convert.ToBase64String(MD5.Create().ComputeHash(data)); - - const string kContentMD5HeaderName = "Content-MD5"; - const string kContentTypeHeaderName = "Content-Type"; - - var httpRequest = new HttpRequestMessage() - { - Method = HttpMethod.Put, - RequestUri = new Uri($"https://{host}/{encodedKey}") - }; - httpRequest.Headers.Host = host; - httpRequest.Headers.Date = DateTimeOffset.Now; - var httpContent = new ByteArrayContent(data); - httpContent.Headers.Add(kContentMD5HeaderName, md5); - httpRequest.Content = httpContent; - - var signedHeaders = new Dictionary<string, string> - { - ["Host"] = host, - [kContentMD5HeaderName] = md5 - }; - - if (contentType != null) - { - httpContent.Headers.Add(kContentTypeHeaderName, contentType); - signedHeaders.Add(kContentTypeHeaderName, contentType); - } - - httpRequest.Headers.TryAddWithoutValidation("Authorization", GenerateSign(GetCredentials(), new RequestInfo - { - Method = "put", - Uri = "/" + encodedKey, - Headers = signedHeaders - }, new TimeDuration(DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(10)))); - - var client = _httpClientFactory.CreateClient(); - - try - { - var response = await client.SendAsync(httpRequest); - if (!response.IsSuccessStatusCode) - throw new Exception($"Not success status code. {response.ToString()}"); - } - catch (Exception e) - { - _logger.LogError(e, "An error occured when test a cos object existence."); - throw; - } - } - - public string GenerateObjectGetUrl(string bucket, string key) - { - if (bucket == null) - throw new ArgumentNullException(nameof(bucket)); - if (key == null) - throw new ArgumentNullException(nameof(key)); - if (!ValidateBucketName(bucket)) - throw new ArgumentException($"Bucket name is not valid. Param is {bucket} .", nameof(bucket)); - - var host = GetHost(bucket); - var encodedKey = WebUtility.UrlEncode(key); - - var signature = GenerateSign(GetCredentials(), new RequestInfo - { - Method = "get", - Uri = "/" + encodedKey, - Headers = new Dictionary<string, string> - { - ["Host"] = host - } - }, new TimeDuration(DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(6))); - - return $"https://{host}/{encodedKey}?{signature}"; - } - } -} |