private static void GetBucketTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.ReadBucket; var requestTemplate = RequestTemplate .FromBucket(bucket); string url = null; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Verify that the URL works initially. var response = await fixture.HttpClient.GetAsync(url); var result = await response.Content.ReadAsStringAsync(); Assert.True(response.IsSuccessStatusCode, result.ToString()); var document = XDocument.Parse(result); var ns = document.Root.GetDefaultNamespace(); var keys = document.Root.Elements(ns + "Contents").Select(contents => contents.Element(ns + "Key").Value).ToList(); var objectNames = await fixture.Client.ListObjectsAsync(bucket, null).Select(o => o.Name).ToListAsync(); Assert.Equal(objectNames, keys); }, afterDelay: async() => { // Verify that the URL no longer works. var response = await fixture.HttpClient.GetAsync(url); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); }, caller); }
private static void ResumableUploadWithCustomerSuppliedEncryptionKeysTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.SingleVersionBucket; var name = IdGenerator.FromGuid(); var requestTemplate = RequestTemplate .FromBucket(bucket) .WithObjectName(name) .WithHttpMethod(ResumableHttpMethod) .WithRequestHeaders(new Dictionary <string, IEnumerable <string> > { { "x-goog-encryption-algorithm", new [] { "AES256" } } }); var content = fixture.SmallContent; string url = null; EncryptionKey key = EncryptionKey.Generate(); fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Verify that the URL works initially. var uploader = SignedUrlResumableUpload.Create( url, new MemoryStream(content), new ResumableUploadOptions { ModifySessionInitiationRequest = key.ModifyRequest }); var progress = await uploader.UploadAsync(); Assert.Null(progress.Exception); Assert.Equal(UploadStatus.Completed, progress.Status); // Make sure the encryption succeeded. var downloadedData = new MemoryStream(); await Assert.ThrowsAsync <GoogleApiException>( () => fixture.Client.DownloadObjectAsync(bucket, name, downloadedData)); await fixture.Client.DownloadObjectAsync(bucket, name, downloadedData, new DownloadObjectOptions { EncryptionKey = key }); AssertContentEqual(content, downloadedData.ToArray()); }, afterDelay: async() => { var uploader = SignedUrlResumableUpload.Create( url, new MemoryStream(content), new ResumableUploadOptions { ModifySessionInitiationRequest = key.ModifyRequest }); // Verify that the URL no longer works. var progress = await uploader.UploadAsync(); Assert.Equal(UploadStatus.Failed, progress.Status); Assert.IsType <GoogleApiException>(progress.Exception); }, caller); }
public void EncryptionKeyAndHashAreIgnored() { var signer = UrlSigner.FromServiceAccountCredential(CreateFakeServiceAccountCredential()); var baseRequestTemplate = RequestTemplate.FromBucket("bucket-name").WithObjectName("object-name"); var options = Options .FromExpiration(DateTimeOffset.UtcNow + TimeSpan.FromDays(1)) .WithSigningVersion(SigningVersion.V2); var algorithmTemplate = baseRequestTemplate.WithRequestHeaders( new Dictionary <string, IEnumerable <string> > { { EncryptionKey.AlgorithmHeader, new [] { EncryptionKey.AlgorithmValue } } }); var keyAndHashTemplate = baseRequestTemplate.WithRequestHeaders( new Dictionary <string, IEnumerable <string> > { { EncryptionKey.AlgorithmHeader, new [] { EncryptionKey.AlgorithmValue } }, { EncryptionKey.KeyHeader, new [] { "abc" } }, { EncryptionKey.KeyHashHeader, new [] { "def" } } }); var url1 = signer.Sign(algorithmTemplate, options); var url2 = signer.Sign(keyAndHashTemplate, options); Assert.Equal(url1, url2); // However, make sure the encryption algorithm is not ignored. var url3 = signer.Sign(baseRequestTemplate, options); Assert.NotEqual(url1, url3); }
private static void HeadWithGetMethodSignedURLTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { Func <HttpRequestMessage> createRequest = null; var requestTemplate = RequestTemplate .FromBucket(fixture.ReadBucket) .WithObjectName(fixture.SmallObject) .WithHttpMethod(HttpMethod.Get); string url = null; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); createRequest = () => new HttpRequestMessage(HttpMethod.Head, url); // Verify that the URL works initially. var response = await fixture.HttpClient.SendAsync(createRequest()); await VerifyResponseAsync(response); }, afterDelay: async() => { // Verify that the URL no longer works. var response = await fixture.HttpClient.SendAsync(createRequest()); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); }, caller); }
public void WithHttpRequestMessage() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); var message = new HttpRequestMessage(HttpMethod.Post, "http://will.be.ignored.com?param1=value1¶m2=value2¶m1=value3"); message.Headers.Add("requestHeader", "value1"); using (MemoryStream stream = new MemoryStream()) { message.Content = new StreamContent(stream); message.Content.Headers.Add("contentHeader", "value2"); } var newRequestTemplate = requestTemplate.WithHttpRequestMessage(message); Assert.NotSame(requestTemplate, newRequestTemplate); Assert.Equal(HttpMethod.Post, newRequestTemplate.HttpMethod); Assert.Collection(newRequestTemplate.RequestHeaders, pair => AssertHeader(pair, "requestHeader", "value1")); Assert.Collection(newRequestTemplate.ContentHeaders, pair => AssertHeader(pair, "contentHeader", "value2")); Assert.Equal(2, newRequestTemplate.QueryParameters.Count); Assert.Equal(2, newRequestTemplate.QueryParameters["param1"].Count); Assert.Equal(1, newRequestTemplate.QueryParameters["param2"].Count); Assert.Equal("value2", newRequestTemplate.QueryParameters["param2"].First()); void AssertHeader(KeyValuePair <string, IReadOnlyCollection <string> > pair, string header, string value) { Assert.Equal(header, pair.Key); Assert.Equal(value, pair.Value.Single()); } }
public async Task Sign_Validations() { var signer = UrlSigner.FromBlobSigner(new FakeBlobSigner()); Assert.Throws <ArgumentNullException>(() => signer.Sign(null, null)); await Assert.ThrowsAsync <ArgumentNullException>(() => signer.SignAsync(null, null)); Assert.Throws <ArgumentNullException>(() => signer.Sign(null, Options.FromDuration(TimeSpan.Zero))); await Assert.ThrowsAsync <ArgumentNullException>(() => signer.SignAsync(null, Options.FromDuration(TimeSpan.Zero))); Assert.Throws <ArgumentNullException>(() => signer.Sign(RequestTemplate.FromBucket("bucket"), null)); await Assert.ThrowsAsync <ArgumentNullException>(() => signer.SignAsync(RequestTemplate.FromBucket("bucket"), null)); // Bucket names cannot be null or contain uppercase letters (among other rules). // Make sure we verify the presence and format of the bucket name in all overloads. Assert.Throws <ArgumentNullException>(() => signer.Sign(null, "objectName", TimeSpan.FromDays(1))); await Assert.ThrowsAsync <ArgumentNullException>(() => signer.SignAsync(null, "objectName", TimeSpan.FromDays(1))); Assert.Throws <ArgumentException>(() => signer.Sign("BUCKETNAME", "objectName", TimeSpan.FromDays(1))); await Assert.ThrowsAsync <ArgumentException>(() => signer.SignAsync("BUCKETNAME", "objectName", TimeSpan.FromDays(1))); // Make sure exceptions are not thrown for things which may be null or uppercase. signer.Sign("bucketname", null, TimeSpan.FromDays(1), null, null); await signer.SignAsync("bucketname", null, TimeSpan.FromDays(1), null, null); }
public override string SavePrivate(string domain, string path, System.IO.Stream stream, DateTime expires) { using var storage = GetStorage(); var objectKey = MakePath(domain, path); var buffered = TempStream.GetBuffered(stream); var uploadObjectOptions = new UploadObjectOptions { PredefinedAcl = PredefinedObjectAcl.BucketOwnerFullControl }; buffered.Position = 0; var uploaded = storage.UploadObject(_bucket, MakePath(domain, path), "application/octet-stream", buffered, uploadObjectOptions, null); uploaded.CacheControl = string.Format("public, maxage={0}", (int)TimeSpan.FromDays(5).TotalSeconds); uploaded.ContentDisposition = "attachment"; if (uploaded.Metadata == null) { uploaded.Metadata = new Dictionary <string, string>(); } uploaded.Metadata["Expires"] = DateTime.UtcNow.Add(TimeSpan.FromDays(5)).ToString("R"); uploaded.Metadata.Add("private-expire", expires.ToFileTimeUtc().ToString(CultureInfo.InvariantCulture)); storage.UpdateObject(uploaded); using var mStream = new MemoryStream(Encoding.UTF8.GetBytes(_json ?? "")); var preSignedURL = FromServiceAccountData(mStream).Sign(RequestTemplate.FromBucket(_bucket).WithObjectName(MakePath(domain, path)), UrlSigner.Options.FromExpiration(expires)); //TODO: CNAME! return(preSignedURL); }
private static void GetObjectWithSpacesTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.SingleVersionBucket; var name = IdGenerator.FromGuid() + " with spaces"; var requestTemplate = RequestTemplate .FromBucket(bucket) .WithObjectName(name); var content = fixture.SmallContent; string url = null; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { fixture.Client.UploadObject(bucket, name, null, new MemoryStream(content)); url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Verify that the URL works initially. var response = await fixture.HttpClient.GetAsync(url); await VerifyResponseAsync(response); var result = await response.Content.ReadAsByteArrayAsync(); AssertContentEqual(content, result); }, afterDelay: async() => { // Verify that the URL no longer works. var response = await fixture.HttpClient.GetAsync(url); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); }, caller); }
private static void GetTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { string url = null; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { url = fixture.UrlSigner.Sign( RequestTemplate.FromBucket(fixture.ReadBucket).WithObjectName(fixture.SmallObject), Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Verify that the URL works initially. var response = await fixture.HttpClient.GetAsync(url); var result = await response.Content.ReadAsByteArrayAsync(); AssertContentEqual(fixture.SmallContent, result); }, afterDelay: async() => { // Verify that the URL no longer works. var response = await fixture.HttpClient.GetAsync(url); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); }, caller); }
private static void PutWithCustomerSuppliedEncryptionKeysTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.SingleVersionBucket; var name = IdGenerator.FromGuid(); var content = fixture.SmallContent; string url = null; EncryptionKey key = EncryptionKey.Generate(); Func <HttpRequestMessage> createPutRequest = () => { var request = new HttpRequestMessage { Method = HttpMethod.Put, Content = new ByteArrayContent(content) }; key.ModifyRequest(request); return(request); }; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { var request = createPutRequest(); var requestTemplate = RequestTemplate .FromBucket(bucket) .WithObjectName(name) .WithHttpRequestMessage(request); url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Verify that the URL works initially. request.RequestUri = new Uri(url); var response = await fixture.HttpClient.SendAsync(request); await VerifyResponseAsync(response); // Make sure the encryption succeeded. var downloadedContent = new MemoryStream(); await Assert.ThrowsAsync <GoogleApiException>( () => fixture.Client.DownloadObjectAsync(bucket, name, downloadedContent)); await fixture.Client.DownloadObjectAsync(bucket, name, downloadedContent, new DownloadObjectOptions { EncryptionKey = key }); AssertContentEqual(content, downloadedContent.ToArray()); }, afterDelay: async() => { // Verify that the URL no longer works. var request = createPutRequest(); request.RequestUri = new Uri(url); var response = await fixture.HttpClient.SendAsync(request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); // Cleanup await fixture.Client.DeleteObjectAsync(bucket, name); }, caller); }
public void WithHttpMethod() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); var newRequestTemplate = requestTemplate.WithHttpMethod(HttpMethod.Post); Assert.NotSame(requestTemplate, newRequestTemplate); Assert.Equal(HttpMethod.Post, newRequestTemplate.HttpMethod); }
public void WithBucket() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); var newRequestTemplate = requestTemplate.WithBucket("another-bucket"); Assert.NotSame(requestTemplate, newRequestTemplate); Assert.Equal("another-bucket", newRequestTemplate.Bucket); }
public void WithObjectName() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); var newRequestTemplate = requestTemplate.WithObjectName("object-name"); Assert.NotSame(requestTemplate, newRequestTemplate); Assert.Equal("object-name", newRequestTemplate.ObjectName); }
public void ExpiryValidation_Invalid(int seconds) { var signer = UrlSigner .FromServiceAccountCredential(StorageConformanceTestData.TestCredential) .WithClock(new FakeClock()); var requestTemplate = RequestTemplate.FromBucket("bucket").WithObjectName("object"); var options = Options.FromDuration(TimeSpan.FromSeconds(seconds)).WithSigningVersion(SigningVersion.V4); Assert.Throws <ArgumentOutOfRangeException>(() => signer.Sign(requestTemplate, options)); }
private static void GetWithCustomerSuppliedEncryptionKeysTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.SingleVersionBucket; var name = IdGenerator.FromGuid(); var content = fixture.SmallContent; string url = null; EncryptionKey key = EncryptionKey.Generate(); Func <HttpRequestMessage> createGetRequest = () => { var request = new HttpRequestMessage { Method = HttpMethod.Get }; key.ModifyRequest(request); return(request); }; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { var encryptingClient = StorageClient.Create(encryptionKey: key); encryptingClient.UploadObject(bucket, name, "application/octet-stream", new MemoryStream(content)); // We don't need to specify the encryption key headers explicitly in the signer template. // The request message we are using in the template already has them set // (by key.ModifyRequest(request)) and the signer will extract them from there. var request = createGetRequest(); var requestTemplate = RequestTemplate .FromBucket(bucket) .WithObjectName(name) .WithHttpRequestMessage(request); url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); request.RequestUri = new Uri(url); // Verify that the URL works initially. var response = await fixture.HttpClient.SendAsync(request); var result = await response.Content.ReadAsByteArrayAsync(); AssertContentEqual(content, result); }, afterDelay: async() => { // Verify that the URL no longer works. var request = createGetRequest(); request.RequestUri = new Uri(url); var response = await fixture.HttpClient.SendAsync(request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); // Cleanup await fixture.Client.DeleteObjectAsync(bucket, name); }, caller); }
public void FromBucket() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); Assert.Equal("bucket-name", requestTemplate.Bucket); Assert.Null(requestTemplate.ObjectName); Assert.Empty(requestTemplate.RequestHeaders); Assert.Empty(requestTemplate.ContentHeaders); Assert.Empty(requestTemplate.QueryParameters); Assert.Equal(HttpMethod.Get, requestTemplate.HttpMethod); }
public void ExpiryValidation_Exactly1Week() { var signer = UrlSigner .FromServiceAccountCredential(StorageConformanceTestData.TestCredential) .WithClock(new FakeClock()); var requestTemplate = RequestTemplate.FromBucket("bucket").WithObjectName("object"); var options = Options.FromDuration(TimeSpan.FromDays(7)).WithSigningVersion(SigningVersion.V4); // Just testing that no exception is thrown. signer.Sign(requestTemplate, options); }
public void BlobSignerSync() { var signer = UrlSigner.FromBlobSigner(new FakeBlobSigner()); var baseRequestTemplate = RequestTemplate.FromBucket("bucket-name").WithObjectName("object-name"); var options = Options .FromExpiration(new DateTime(1970, 1, 1, 0, 0, 30, DateTimeKind.Utc)) .WithSigningVersion(SigningVersion.V2); var url = signer.Sign(baseRequestTemplate, options); Assert.Equal("https://storage.googleapis.com/bucket-name/object-name?GoogleAccessId=FakeId&Expires=30&Signature=AAA%3D", url); }
/// <summary> /// Creates a signed URL which can be used to provide limited access to specific buckets and objects to anyone /// in possession of the URL, regardless of whether they have a Google account. /// </summary> /// <remarks> /// <para> /// When a <see cref="UrlSigner"/> is created with a service account credential, the signing can be performed /// with no network access. When it is created with an implementation of <see cref="IBlobSigner"/>, that may require /// network access or other IO. In that case, one of the asynchronous methods should be used when the caller is /// in a context that should not block. /// </para> /// <para> /// See https://cloud.google.com/storage/docs/access-control/signed-urls for more information on signed URLs. /// </para> /// <para> /// Note that when GET is specified as the <paramref name="httpMethod"/> (or it is null, in which case GET is /// used), both GET and HEAD requests can be made with the created signed URL. /// </para> /// </remarks> /// <param name="bucket">The name of the bucket. Must not be null.</param> /// <param name="objectName">The name of the object within the bucket. May be null, in which case the signed URL /// will refer to the bucket instead of an object.</param> /// <param name="duration">The length of time for which the signed URL should remain usable.</param> /// <param name="httpMethod">The HTTP request method for which the signed URL is allowed to be used. May be null, /// in which case GET will be used.</param> /// <param name="signingVersion">The signing version to use to generate the signed URL. May be null, in which case /// <see cref="SigningVersion.Default"/> will be used.</param> /// <param name="cancellationToken">The token to monitor for cancellation requests.</param> /// <returns> A task representing the asynchronous operation, with a result returning the /// signed URL which can be used to provide access to a bucket or object for a limited amount of time.</returns> public async Task <string> SignAsync(string bucket, string objectName, TimeSpan duration, HttpMethod httpMethod = null, SigningVersion?signingVersion = null, CancellationToken cancellationToken = default) { var template = RequestTemplate .FromBucket(bucket) .WithObjectName(objectName) .WithHttpMethod(httpMethod); var options = Options.FromDuration(duration); if (signingVersion.HasValue) { options = options.WithSigningVersion(signingVersion.Value); } return(await SignAsync(template, options, cancellationToken).ConfigureAwait(false)); }
/// <summary> /// Creates a signed URL which can be used to provide limited access to specific buckets and objects to anyone /// in possession of the URL, regardless of whether they have a Google account. /// </summary> /// <remarks> /// <para> /// When a <see cref="UrlSigner"/> is created with a service account credential, the signing can be performed /// with no network access. When it is created with an implementation of <see cref="IBlobSigner"/>, that may require /// network access or other IO. In that case, one of the asynchronous methods should be used when the caller is /// in a context that should not block. /// </para> /// <para> /// See https://cloud.google.com/storage/docs/access-control/signed-urls for more information on signed URLs. /// </para> /// <para> /// Note that when GET is specified as the <paramref name="httpMethod"/> (or it is null, in which case GET is /// used), both GET and HEAD requests can be made with the created signed URL. /// </para> /// </remarks> /// <param name="bucket">The name of the bucket. Must not be null.</param> /// <param name="objectName">The name of the object within the bucket. May be null, in which case the signed URL /// will refer to the bucket instead of an object.</param> /// <param name="duration">The length of time for which the signed URL should remain usable.</param> /// <param name="httpMethod">The HTTP request method for which the signed URL is allowed to be used. May be null, /// in which case GET will be used.</param> /// <param name="signingVersion">The signing version to use to generate the signed URL. May be null, in which case /// <see cref="SigningVersion.Default"/> will be used.</param> /// <returns>The signed URL which can be used to provide access to a bucket or object for a limited amount of time.</returns> public string Sign(string bucket, string objectName, TimeSpan duration, HttpMethod httpMethod = null, SigningVersion?signingVersion = null) { var template = RequestTemplate .FromBucket(bucket) .WithObjectName(objectName) .WithHttpMethod(httpMethod); var options = Options.FromDuration(duration); if (signingVersion.HasValue) { options = options.WithSigningVersion(signingVersion.Value); } return(Sign(template, options)); }
public async Task ThrowsIfBucketBoundHostSpecified() { var signer = UrlSigner.FromServiceAccountCredential(CreateFakeServiceAccountCredential()); var requestTemplate = RequestTemplate .FromBucket("bucket-name") .WithObjectName("object-name"); var options = Options .FromExpiration(DateTimeOffset.UtcNow + TimeSpan.FromDays(1)) .WithSigningVersion(SigningVersion.V2) .WithBucketBoundHostname("my.bucket.domain"); Assert.Throws <ArgumentOutOfRangeException>(() => signer.Sign(requestTemplate, options)); await Assert.ThrowsAsync <ArgumentOutOfRangeException>(() => signer.SignAsync(requestTemplate, options)); }
public async Task ThrowsIfQueryParametersSpecified() { var signer = UrlSigner.FromServiceAccountCredential(CreateFakeServiceAccountCredential()); var requestTemplate = RequestTemplate .FromBucket("bucket-name") .WithObjectName("object-name") .WithQueryParameters( new Dictionary <string, IEnumerable <string> > { { "param1", new string[] { "value1" } } }); var options = Options .FromExpiration(DateTimeOffset.UtcNow + TimeSpan.FromDays(1)) .WithSigningVersion(SigningVersion.V2); Assert.Throws <ArgumentException>(() => signer.Sign(requestTemplate, options)); await Assert.ThrowsAsync <ArgumentException>(() => signer.SignAsync(requestTemplate, options)); }
public void SigningTest(SigningV4Test test) { var timestamp = test.Timestamp.ToDateTime(); var clock = new FakeClock(timestamp); var signer = UrlSigner .FromServiceAccountCredential(StorageConformanceTestData.TestCredential) .WithClock(clock); var requestTemplate = RequestTemplate .FromBucket(test.Bucket) .WithObjectName(test.Object) .WithHttpMethod(s_methods[test.Method]) .WithRequestHeaders(test.Headers.ToDictionary(kvp => kvp.Key, kvp => Enumerable.Repeat(kvp.Value, 1))) .WithQueryParameters(test.QueryParameters.ToDictionary(kvp => kvp.Key, kvp => Enumerable.Repeat(kvp.Value, 1))); var options = Options .FromDuration(TimeSpan.FromSeconds(test.Expiration)) .WithSigningVersion(SigningVersion.V4) .WithScheme(test.Scheme); switch (test.UrlStyle) { case UrlStyle.VirtualHostedStyle: options = options.WithUrlStyle(UrlSigner.UrlStyle.VirtualHostedStyle); break; case UrlStyle.BucketBoundHostname: options = options.WithBucketBoundHostname(test.BucketBoundHostname); break; default: break; } var actualUrl = signer.Sign(requestTemplate, options); // We almost always want the complete URL afterwards, which xUnit doesn't give us. if (test.ExpectedUrl != actualUrl) { FileLogger.Log($"{test.Description} failure"); FileLogger.Log($"Expected: {test.ExpectedUrl}"); FileLogger.Log($"Actual: {actualUrl}"); } Assert.Equal(test.ExpectedUrl, actualUrl); }
private static void ResumableUploadTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.SingleVersionBucket; var name = IdGenerator.FromGuid(); var requestTemplate = RequestTemplate .FromBucket(bucket) .WithObjectName(name) .WithHttpMethod(ResumableHttpMethod); var content = fixture.SmallContent; string url = null; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Verify that the URL works initially. var uploader = SignedUrlResumableUpload.Create(url, new MemoryStream(content)); var progress = await uploader.UploadAsync(); Assert.Equal(UploadStatus.Completed, progress.Status); var result = new MemoryStream(); await fixture.Client.DownloadObjectAsync(bucket, name, result); AssertContentEqual(content, result.ToArray()); // Reset the state. await fixture.Client.DeleteObjectAsync(bucket, name); }, afterDelay: async() => { var uploader = SignedUrlResumableUpload.Create(url, new MemoryStream(content)); // Verify that the URL no longer works. var progress = await uploader.UploadAsync(); Assert.Equal(UploadStatus.Failed, progress.Status); Assert.IsType <GoogleApiException>(progress.Exception); var obj = await fixture.Client.ListObjectsAsync(bucket, name).FirstOrDefaultAsync(o => o.Name == name); Assert.Null(obj); }, caller); }
private static void GetWithCustomHeadersTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { string url = null; Func <HttpRequestMessage> createRequest = () => new HttpRequestMessage() { Method = HttpMethod.Get, Headers = { { "x-goog-foo", "xy\r\n z" }, { "x-goog-bar", " 12345 " }, { "x-goog-foo", new [] { "A B C", "def"} } } }; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { var request = createRequest(); var requestTemplate = RequestTemplate .FromBucket(fixture.ReadBucket) .WithObjectName(fixture.SmallObject) .WithHttpRequestMessage(request); url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); request.RequestUri = new Uri(url); // Verify that the URL works initially. var response = await fixture.HttpClient.SendAsync(request); var result = await response.Content.ReadAsByteArrayAsync(); AssertContentEqual(fixture.SmallContent, result); }, afterDelay: async() => { // Verify that the URL no longer works. var request = createRequest(); request.RequestUri = new Uri(url); var response = await fixture.HttpClient.SendAsync(request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); }, caller); }
public void WithContentHeaders() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); var headers = new Dictionary <string, IEnumerable <string> > { { "header1", new List <string> { "value1", "value2" } }, { "header2", new List <string> { "value3" } } }; var newRequestTemplate = requestTemplate.WithContentHeaders(headers); var expectedHeaders = ToExpectedEntries(headers); Assert.NotSame(requestTemplate, newRequestTemplate); Assert.Equal(expectedHeaders, newRequestTemplate.ContentHeaders); }
public void WithQueryParameters() { var requestTemplate = RequestTemplate.FromBucket("bucket-name"); var queryParameters = new Dictionary <string, IEnumerable <string> > { { "query1", new List <string> { "value1", "value2" } }, { "query2", new List <string> { "value3" } } }; var newRequestTemplate = requestTemplate.WithQueryParameters(queryParameters); var expectedParameters = ToExpectedEntries(queryParameters); Assert.NotSame(requestTemplate, newRequestTemplate); Assert.Equal(expectedParameters, newRequestTemplate.QueryParameters); }
private static void DeleteTest_Common(StorageFixture fixture, SigningVersion signingVersion, [CallerMemberName] string caller = null) { var bucket = fixture.SingleVersionBucket; var name = IdGenerator.FromGuid(); var requestTemplate = RequestTemplate .FromBucket(bucket) .WithObjectName(name) .WithHttpMethod(HttpMethod.Delete); string url = null; fixture.RegisterDelayTest( s_duration, beforeDelay: async duration => { url = fixture.UrlSigner.Sign(requestTemplate, Options.FromDuration(duration).WithSigningVersion(signingVersion)); // Upload an object which can be deleted with the URL. await fixture.Client.UploadObjectAsync(bucket, name, "", new MemoryStream(fixture.SmallContent)); // Verify that the URL works initially. var response = await fixture.HttpClient.DeleteAsync(url); await VerifyResponseAsync(response); var obj = await fixture.Client.ListObjectsAsync(bucket, name).FirstOrDefaultAsync(o => o.Name == name); Assert.Null(obj); // Restore the object. await fixture.Client.UploadObjectAsync(bucket, name, "", new MemoryStream(fixture.SmallContent)); }, afterDelay: async() => { // Verify that the URL no longer works. var response = await fixture.HttpClient.DeleteAsync(url); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); var obj = await fixture.Client.ListObjectsAsync(bucket, name).FirstOrDefaultAsync(o => o.Name == name); Assert.NotNull(obj); // Cleanup await fixture.Client.DeleteObjectAsync(bucket, name); }, caller); }
public void SampleRequest() { var clock = new FakeClock(new DateTime(2018, 11, 19, 5, 56, 54, DateTimeKind.Utc)); var requestTemplate = RequestTemplate.FromBucket("jessefrank2").WithObjectName("kitten.png"); var options = Options.FromDuration(TimeSpan.FromHours(1)).WithSigningVersion(SigningVersion.V4); var serviceAccount = CreateFakeServiceAccountCredential("*****@*****.**"); var signer = UrlSigner .FromServiceAccountCredential(serviceAccount) .WithClock(clock); var uriString = signer.Sign(requestTemplate, options); var parameters = ExtractQueryParameters(uriString); Assert.Equal("GOOG4-RSA-SHA256", parameters["X-Goog-Algorithm"]); Assert.Equal("test-account%40spec-test-ruby-samples.iam.gserviceaccount.com%2F20181119%2Fauto%2Fstorage%2Fgoog4_request", parameters["X-Goog-Credential"]); Assert.Equal("20181119T055654Z", parameters["X-Goog-Date"]); Assert.Equal("3600", parameters["X-Goog-Expires"]); Assert.Equal("host", parameters["X-Goog-SignedHeaders"]); // No check for the exact signature. }
public void ResumableEquivalentToPostWithStartHeader() { var signer = UrlSigner.FromServiceAccountCredential(CreateFakeServiceAccountCredential()); var baseRequestTemplate = RequestTemplate.FromBucket("bucket-name").WithObjectName("object-name"); var options = Options .FromExpiration(DateTimeOffset.UtcNow + TimeSpan.FromDays(1)) .WithSigningVersion(SigningVersion.V2); var resumableTemplate = baseRequestTemplate.WithHttpMethod(ResumableHttpMethod); var startHeaderTemplate = baseRequestTemplate .WithHttpMethod(HttpMethod.Post) .WithRequestHeaders( new Dictionary <string, IEnumerable <string> > { { "x-goog-resumable", new[] { "start" } } }); var url1 = signer.Sign(resumableTemplate, options); var url2 = signer.Sign(startHeaderTemplate, options); Assert.Equal(url1, url2); }