Skip to content

Commit 6080e08

Browse files
author
Daniel Jacinto
authored
[SAS Token] Create get only uri methods for files. (#8424)
* support for returning only file uri and refactor of GetBlobForUriAsync. * GetPackageUriAsync test cases. * nit.
1 parent c107688 commit 6080e08

7 files changed

Lines changed: 147 additions & 18 deletions

File tree

src/NuGetGallery.Core/Services/CloudBlobCoreFileStorageService.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -368,13 +368,25 @@ public async Task SaveFileAsync(string folderName, string fileName, Stream file,
368368
await blob.SetPropertiesAsync();
369369
}
370370

371+
public async Task<Uri> GetFileUriAsync(string folderName, string fileName)
372+
{
373+
var blob = await GetBlobForUriAsync(folderName, fileName);
374+
375+
return blob.Uri;
376+
}
377+
371378
public async Task<Uri> GetPriviledgedFileUriAsync(
372379
string folderName,
373380
string fileName,
374381
FileUriPermissions permissions,
375382
DateTimeOffset endOfAccess)
376383
{
377-
var blob = await GetBlobForUriAsync(folderName, fileName, endOfAccess);
384+
if (endOfAccess < DateTimeOffset.UtcNow)
385+
{
386+
throw new ArgumentOutOfRangeException(nameof(endOfAccess), $"{nameof(endOfAccess)} is in the past");
387+
}
388+
389+
var blob = await GetBlobForUriAsync(folderName, fileName);
378390

379391
return new Uri(
380392
blob.Uri,
@@ -383,13 +395,23 @@ public async Task<Uri> GetPriviledgedFileUriAsync(
383395

384396
public async Task<Uri> GetFileReadUriAsync(string folderName, string fileName, DateTimeOffset? endOfAccess)
385397
{
386-
var blob = await GetBlobForUriAsync(folderName, fileName, endOfAccess);
398+
var blob = await GetBlobForUriAsync(folderName, fileName);
387399

388400
if (IsPublicContainer(folderName))
389401
{
390402
return blob.Uri;
391403
}
392404

405+
if (!endOfAccess.HasValue)
406+
{
407+
throw new ArgumentNullException(nameof(endOfAccess), $"{nameof(endOfAccess)} must not be null for non-public containers");
408+
}
409+
410+
if (endOfAccess < DateTimeOffset.UtcNow)
411+
{
412+
throw new ArgumentOutOfRangeException(nameof(endOfAccess), $"{nameof(endOfAccess)} is in the past");
413+
}
414+
393415
return new Uri(
394416
blob.Uri,
395417
blob.GetSharedAccessSignature(SharedAccessBlobPermissions.Read, endOfAccess));
@@ -519,20 +541,11 @@ private static SharedAccessBlobPermissions MapFileUriPermissions(FileUriPermissi
519541
return (SharedAccessBlobPermissions)permissions;
520542
}
521543

522-
private async Task<ISimpleCloudBlob> GetBlobForUriAsync(string folderName, string fileName, DateTimeOffset? endOfAccess)
544+
private async Task<ISimpleCloudBlob> GetBlobForUriAsync(string folderName, string fileName)
523545
{
524546
folderName = folderName ?? throw new ArgumentNullException(nameof(folderName));
525547
fileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
526-
if (endOfAccess.HasValue && endOfAccess < DateTimeOffset.UtcNow)
527-
{
528-
throw new ArgumentOutOfRangeException(nameof(endOfAccess), $"{nameof(endOfAccess)} is in the past");
529-
}
530-
531-
if (!IsPublicContainer(folderName) && endOfAccess == null)
532-
{
533-
throw new ArgumentNullException(nameof(endOfAccess), $"{nameof(endOfAccess)} must not be null for non-public containers");
534-
}
535-
548+
536549
ICloudBlobContainer container = await GetContainerAsync(folderName);
537550

538551
return container.GetBlobReference(fileName);

src/NuGetGallery.Core/Services/CorePackageFileService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ public Task<Stream> DownloadPackageFileAsync(Package package)
4444
return _fileStorageService.GetFileAsync(_metadata.FileFolderName, fileName);
4545
}
4646

47+
public Task<Uri> GetPackageUriAsync(Package package)
48+
{
49+
var fileName = FileNameHelper.BuildFileName(package, _metadata.FileSavePathTemplate, _metadata.FileExtension);
50+
return _fileStorageService.GetFileUriAsync(_metadata.FileFolderName, fileName);
51+
}
52+
4753
public Task<Uri> GetPackageReadUriAsync(Package package)
4854
{
4955
var fileName = FileNameHelper.BuildFileName(package, _metadata.FileSavePathTemplate, _metadata.FileExtension);

src/NuGetGallery.Core/Services/ICoreFileStorageService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ public interface ICoreFileStorageService
2525
/// <param name="ifNoneMatch">The <see cref="IFileReference.ContentId"/> value to use in an If-None-Match request</param>
2626
/// <returns>A <see cref="IFileReference"/> representing the file reference</returns>
2727
Task<IFileReference> GetFileReferenceAsync(string folderName, string fileName, string ifNoneMatch = null);
28+
29+
/// <summary>
30+
/// Gets a file URI.
31+
/// </summary>
32+
/// <param name="folderName">The folder containing the file.</param>
33+
/// <param name="fileName">The file within the <paramref name="folderName"/>.</param>
34+
/// <returns>A <see cref="Uri"/> for the specified file.</returns>
35+
Task<Uri> GetFileUriAsync(string folderName, string fileName);
2836

2937
/// <summary>
3038
/// Generates the storage file URI (which is optionally time limited)

src/NuGetGallery.Core/Services/ICorePackageFileService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ public interface ICorePackageFileService
2525
/// </summary>
2626
Task<Stream> DownloadPackageFileAsync(Package package);
2727

28+
/// <summary>
29+
/// Generates the URL for the specified package.
30+
/// </summary>
31+
/// <param name="package">The package metadata.</param>
32+
/// <returns>Package URL</returns>
33+
Task<Uri> GetPackageUriAsync(Package package);
34+
2835
/// <summary>
2936
/// Generates the URL for the specified package in the public container for available packages.
3037
/// </summary>

src/NuGetGallery/Services/FileSystemFileStorageService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ public Task<bool> IsAvailableAsync()
237237
return Task.FromResult(Directory.Exists(_configuration.FileStorageDirectory));
238238
}
239239

240+
public Task<Uri> GetFileUriAsync(string folderName, string fileName)
241+
{
242+
/// Not implemented for the same reason as <see cref="GetFileReadUriAsync(string, string, DateTimeOffset?)"/>.
243+
throw new NotImplementedException();
244+
}
245+
240246
public Task<Uri> GetFileReadUriAsync(string folderName, string fileName, DateTimeOffset? endOfAccess)
241247
{
242248
// technically, we would be able to generate the file:/// url here, but we don't need it right now

tests/NuGetGallery.Core.Facts/Services/CloudBlobCoreFileStorageServiceFacts.cs

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,64 @@ public async Task WillSetTheBlobContentType(string folderName)
673673
}
674674
}
675675

676+
public class TheGetFileUriAsyncMethod
677+
{
678+
private const string folderName = "theFolderName";
679+
private const string fileName = "theFileName";
680+
681+
[Fact]
682+
public async Task WillThrowIfFolderIsNull()
683+
{
684+
var service = CreateService();
685+
686+
var ex = await Assert.ThrowsAsync<ArgumentNullException>(() => service.GetFileUriAsync(null, fileName));
687+
Assert.Equal("folderName", ex.ParamName);
688+
}
689+
690+
[Fact]
691+
public async Task WillThrowIfFilenameIsNull()
692+
{
693+
var service = CreateService();
694+
695+
var ex = await Assert.ThrowsAsync<ArgumentNullException>(() => service.GetFileUriAsync(folderName, null));
696+
Assert.Equal("fileName", ex.ParamName);
697+
}
698+
699+
[Fact]
700+
public async Task WillAlwaysReturnValidUri()
701+
{
702+
var containerName = CoreConstants.Folders.ValidationFolderName;
703+
var expectedUri = $"http://example.com/{CoreConstants.Folders.ValidationFolderName}/{fileName}";
704+
705+
var setupResult = Setup(containerName, fileName);
706+
var fakeBlobClient = setupResult.Item1;
707+
708+
var service = CreateService(fakeBlobClient);
709+
710+
var uri = await service.GetFileUriAsync(containerName, fileName);
711+
712+
Assert.Equal(expectedUri, uri.AbsoluteUri);
713+
}
714+
715+
private static Tuple<Mock<ICloudBlobClient>, Mock<ISimpleCloudBlob>, Uri> Setup(string folderName, string fileName)
716+
{
717+
var fakeBlobClient = new Mock<ICloudBlobClient>();
718+
var fakeContainer = new Mock<ICloudBlobContainer>();
719+
fakeBlobClient
720+
.Setup(bc => bc.GetContainerReference(folderName))
721+
.Returns(fakeContainer.Object)
722+
.Callback(() => { int i = 0; i = i + 1; });
723+
var fakeBlob = new Mock<ISimpleCloudBlob>();
724+
fakeContainer.Setup(c => c.GetBlobReference(fileName)).Returns(fakeBlob.Object);
725+
726+
var blobUri = new Uri($"http://example.com/{folderName}/{fileName}");
727+
728+
fakeBlob.SetupGet(b => b.Uri).Returns(blobUri);
729+
730+
return Tuple.Create(fakeBlobClient, fakeBlob, blobUri);
731+
}
732+
}
733+
676734
public class TheGetPriviledgedFileUriAsyncMethod
677735
{
678736
private const string folderName = "theFolderName";
@@ -824,12 +882,18 @@ public async Task WillThrowIfFilenameIsNull()
824882
}
825883

826884
[Fact]
827-
public async Task WillThrowIfEndOfAccessIsInThePast()
885+
public async Task WillThrowIfEndOfAccessIsInThePastForNonPublicContainer()
828886
{
829-
var service = CreateService();
830-
887+
var setupResult = Setup(folderName, fileName);
888+
var fakeFolderInformationProvider = new Mock<ICloudBlobContainerInformationProvider>();
889+
fakeFolderInformationProvider
890+
.Setup(fip => fip.IsPublicContainer(It.IsAny<string>()))
891+
.Returns(false);
892+
var service = CreateService(setupResult.Item1, fakeFolderInformationProvider);
831893
DateTimeOffset inThePast = DateTimeOffset.UtcNow.AddSeconds(-1);
894+
832895
var ex = await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => service.GetFileReadUriAsync(folderName, fileName, inThePast));
896+
833897
Assert.Equal("endOfAccess", ex.ParamName);
834898
}
835899

@@ -863,11 +927,12 @@ public async Task WillUseSasTokenDependingOnContainerAvailability(bool isPublicC
863927
[Fact]
864928
public async Task WillThrowIfNoEndOfAccessSpecifiedForNonPublicContainer()
865929
{
930+
var setupResult = Setup(CoreConstants.Folders.ValidationFolderName, fileName);
866931
var fakeFolderInformationProvider = new Mock<ICloudBlobContainerInformationProvider>();
867932
fakeFolderInformationProvider
868933
.Setup(fip => fip.IsPublicContainer(It.IsAny<string>()))
869934
.Returns(false);
870-
var service = CreateService(fakeFolderInformationProvider: fakeFolderInformationProvider);
935+
var service = CreateService(setupResult.Item1, fakeFolderInformationProvider);
871936

872937
var ex = await Assert.ThrowsAsync<ArgumentNullException>(() => service.GetFileReadUriAsync(CoreConstants.Folders.ValidationFolderName, fileName, null));
873938
Assert.Equal("endOfAccess", ex.ParamName);
@@ -1596,6 +1661,5 @@ public async Task VerifyETagIsNullWhenBlobDoesNotExist()
15961661
Assert.Null(etagValue);
15971662
}
15981663
}
1599-
16001664
}
16011665
}

tests/NuGetGallery.Core.Facts/Services/CorePackageFileServiceFacts.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,31 @@ public async Task WillUseTheFileStorageService()
419419
}
420420
}
421421

422+
public class TheGetPackageUriMethod : FactsBase
423+
{
424+
[Fact]
425+
public async Task WillThrowIfPackageIsNull()
426+
{
427+
var ex = await Assert.ThrowsAsync<ArgumentNullException>(() => _service.GetPackageUriAsync(null));
428+
Assert.Equal("package", ex.ParamName);
429+
}
430+
431+
[Fact]
432+
public async Task WillUseFileStorageService()
433+
{
434+
await _service.GetPackageUriAsync(_package);
435+
436+
string filename = BuildFileName(_package.PackageRegistration.Id, _package.NormalizedVersion, CoreConstants.NuGetPackageFileExtension, CoreConstants.PackageFileSavePathTemplate);
437+
438+
_fileStorageService.Verify(
439+
x => x.GetFileUriAsync(PackagesFolderName, filename),
440+
Times.Once());
441+
_fileStorageService.Verify(
442+
x => x.GetFileUriAsync(It.IsAny<string>(), It.IsAny<string>()),
443+
Times.Once());
444+
}
445+
}
446+
422447
public class TheGetPackageReadUriMethod : FactsBase
423448
{
424449
[Fact]

0 commit comments

Comments
 (0)