Skip to content

Commit 77c0b16

Browse files
author
Scott Bommarito
authored
ApiController actions should not throw exceptions when called with an unparseable version (#6089)
1 parent 6dcc0a9 commit 77c0b16

3 files changed

Lines changed: 53 additions & 28 deletions

File tree

src/NuGetGallery/Controllers/ApiController.cs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,14 +1039,9 @@ public virtual async Task<ActionResult> GetStatsDownloads(int? count)
10391039
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
10401040
}
10411041

1042-
private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluation(ApiScopeEvaluationResult evaluationResult, string id, string version)
1042+
private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluation(ApiScopeEvaluationResult evaluationResult, string id, string versionString)
10431043
{
1044-
return GetHttpResultFromFailedApiScopeEvaluation(evaluationResult, id, NuGetVersion.Parse(version));
1045-
}
1046-
1047-
private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluation(ApiScopeEvaluationResult result, string id, NuGetVersion version)
1048-
{
1049-
return GetHttpResultFromFailedApiScopeEvaluationHelper(result, id, version, HttpStatusCode.Forbidden);
1044+
return GetHttpResultFromFailedApiScopeEvaluationHelper(evaluationResult, id, versionString, HttpStatusCode.Forbidden);
10501045
}
10511046

10521047
/// <remarks>
@@ -1055,10 +1050,10 @@ private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluation(A
10551050
/// </remarks>
10561051
private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluationForPush(ApiScopeEvaluationResult result, string id, NuGetVersion version)
10571052
{
1058-
return GetHttpResultFromFailedApiScopeEvaluationHelper(result, id, version, HttpStatusCode.Unauthorized);
1053+
return GetHttpResultFromFailedApiScopeEvaluationHelper(result, id, version.ToNormalizedString(), HttpStatusCode.Unauthorized);
10591054
}
10601055

1061-
private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluationHelper(ApiScopeEvaluationResult result, string id, NuGetVersion version, HttpStatusCode statusCodeOnFailure)
1056+
private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluationHelper(ApiScopeEvaluationResult result, string id, string versionString, HttpStatusCode statusCodeOnFailure)
10621057
{
10631058
if (result.IsSuccessful())
10641059
{
@@ -1068,7 +1063,7 @@ private HttpStatusCodeWithBodyResult GetHttpResultFromFailedApiScopeEvaluationHe
10681063
if (result.PermissionsCheckResult == PermissionsCheckResult.ReservedNamespaceFailure)
10691064
{
10701065
// We return a special error code for reserved namespace failures.
1071-
TelemetryService.TrackPackagePushNamespaceConflictEvent(id, version.ToNormalizedString(), GetCurrentUser(), User.Identity);
1066+
TelemetryService.TrackPackagePushNamespaceConflictEvent(id, versionString, GetCurrentUser(), User.Identity);
10721067
return new HttpStatusCodeWithBodyResult(HttpStatusCode.Conflict, Strings.UploadPackage_IdNamespaceConflict);
10731068
}
10741069

tests/NuGetGallery.Facts/Controllers/ApiControllerFacts.cs

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,12 +1235,13 @@ public async Task WillNotCreateAPackageIfScopesInvalidWithExistingRegistration(A
12351235
{
12361236
// Arrange
12371237
var packageId = "theId";
1238+
var packageVersion = "1.0.42";
12381239
var packageRegistration = new PackageRegistration { Id = packageId };
12391240
packageRegistration.Id = packageId;
12401241
var package = new Package
12411242
{
12421243
PackageRegistration = packageRegistration,
1243-
Version = "1.0.42"
1244+
Version = packageVersion
12441245
};
12451246
packageRegistration.Packages.Add(package);
12461247

@@ -1252,7 +1253,7 @@ public async Task WillNotCreateAPackageIfScopesInvalidWithExistingRegistration(A
12521253
var currentUser = fakes.User;
12531254
controller.SetCurrentUser(currentUser);
12541255

1255-
var nuGetPackage = TestPackage.CreateTestPackageStream(packageId, "1.0.42");
1256+
var nuGetPackage = TestPackage.CreateTestPackageStream(packageId, packageVersion);
12561257
controller.SetupPackageFromInputStream(nuGetPackage);
12571258

12581259
controller.MockApiScopeEvaluator
@@ -1627,19 +1628,22 @@ public async Task WillThrowIfAPackageWithTheIdAndNuGetVersionDoesNotExist()
16271628
controller.MockPackageService.Verify(x => x.MarkPackageUnlistedAsync(It.IsAny<Package>(), true), Times.Never());
16281629
}
16291630

1630-
public static IEnumerable<object[]> WillNotUnlistThePackageIfScopesInvalid_Data => InvalidScopes_Data;
1631+
public static IEnumerable<object[]> WillNotUnlistThePackageIfScopesInvalid_Data => MemberDataHelper.Combine(
1632+
InvalidScopes_Data,
1633+
MemberDataHelper.AsDataSet("1.0.42", "invalidPackageVersion"));
16311634

16321635
[Theory]
16331636
[MemberData(nameof(WillNotUnlistThePackageIfScopesInvalid_Data))]
1634-
public async Task WillNotUnlistThePackageIfScopesInvalid(ApiScopeEvaluationResult evaluationResult, HttpStatusCode expectedStatusCode, string description)
1637+
public async Task WillNotUnlistThePackageIfScopesInvalid(ApiScopeEvaluationResult evaluationResult, HttpStatusCode expectedStatusCode, string description, string version)
16351638
{
16361639
var fakes = Get<Fakes>();
16371640
var currentUser = fakes.User;
16381641

16391642
var id = "theId";
16401643
var package = new Package
16411644
{
1642-
PackageRegistration = new PackageRegistration { Id = id }
1645+
PackageRegistration = new PackageRegistration { Id = id },
1646+
Version = version
16431647
};
16441648

16451649
var controller = new TestableApiController(GetConfigurationService());
@@ -1656,7 +1660,7 @@ public async Task WillNotUnlistThePackageIfScopesInvalid(ApiScopeEvaluationResul
16561660
NuGetScopes.PackageUnlist))
16571661
.Returns(evaluationResult);
16581662

1659-
var result = await controller.DeletePackage("theId", "1.0.42");
1663+
var result = await controller.DeletePackage(id, version);
16601664

16611665
ResultAssert.IsStatusCode(
16621666
result,
@@ -2046,19 +2050,22 @@ public async Task WillThrowIfAPackageWithTheIdAndNuGetVersionDoesNotExist()
20462050
controller.MockPackageService.Verify(x => x.MarkPackageListedAsync(It.IsAny<Package>(), It.IsAny<bool>()), Times.Never());
20472051
}
20482052

2049-
public static IEnumerable<object[]> WillListThePackageIfScopesInvalid_Data => InvalidScopes_Data;
2053+
public static IEnumerable<object[]> WillNotListThePackageIfScopesInvalid_Data => MemberDataHelper.Combine(
2054+
InvalidScopes_Data,
2055+
MemberDataHelper.AsDataSet("1.0.42", "invalidPackageVersion"));
20502056

20512057
[Theory]
2052-
[MemberData(nameof(WillListThePackageIfScopesInvalid_Data))]
2053-
public async Task WillListThePackageIfScopesInvalid(ApiScopeEvaluationResult evaluationResult, HttpStatusCode expectedStatusCode, string description)
2058+
[MemberData(nameof(WillNotListThePackageIfScopesInvalid_Data))]
2059+
public async Task WillNotListThePackageIfScopesInvalid(ApiScopeEvaluationResult evaluationResult, HttpStatusCode expectedStatusCode, string description, string version)
20542060
{
20552061
var fakes = Get<Fakes>();
20562062
var currentUser = fakes.User;
20572063

20582064
var id = "theId";
20592065
var package = new Package
20602066
{
2061-
PackageRegistration = new PackageRegistration { Id = id }
2067+
PackageRegistration = new PackageRegistration { Id = id },
2068+
Version = version
20622069
};
20632070

20642071
var controller = new TestableApiController(GetConfigurationService());
@@ -2075,7 +2082,7 @@ public async Task WillListThePackageIfScopesInvalid(ApiScopeEvaluationResult eva
20752082
NuGetScopes.PackageUnlist))
20762083
.Returns(evaluationResult);
20772084

2078-
var result = await controller.PublishPackage("theId", "1.0.42");
2085+
var result = await controller.PublishPackage(id, version);
20792086

20802087
ResultAssert.IsStatusCode(
20812088
result,
@@ -2352,31 +2359,42 @@ public async Task Returns404IfPackageDoesNotExist(string credentialType)
23522359
It.IsAny<User>(), controller.OwinContext.Request.User.Identity, 404), Times.Once);
23532360
}
23542361

2362+
public static IEnumerable<object[]> Returns403IfScopeDoesNotMatch_PackageVersion_Data =>
2363+
MemberDataHelper.AsDataSet("1.0.42", "invalidVersionString");
2364+
23552365
public static IEnumerable<object[]> Returns403IfScopeDoesNotMatch_Data => InvalidScopes_Data;
23562366

23572367
public static IEnumerable<object[]> Returns403IfScopeDoesNotMatch_NotVerify_Data
23582368
{
23592369
get
23602370
{
2361-
var notVerifyData = CredentialTypesExceptVerifyV1.Select(t => new object[] { t, new[] { NuGetScopes.PackagePush, NuGetScopes.PackagePushVersion } });
2362-
return MemberDataHelper.Combine(notVerifyData, Returns403IfScopeDoesNotMatch_Data);
2371+
var notVerifyData = CredentialTypesExceptVerifyV1.Select(
2372+
t => MemberDataHelper.AsData(t, new[] { NuGetScopes.PackagePush, NuGetScopes.PackagePushVersion }));
2373+
return MemberDataHelper.Combine(
2374+
notVerifyData,
2375+
Returns403IfScopeDoesNotMatch_Data,
2376+
Returns403IfScopeDoesNotMatch_PackageVersion_Data);
23632377
}
23642378
}
23652379

23662380
public static IEnumerable<object[]> Returns403IfScopeDoesNotMatch_Verify_Data
23672381
{
23682382
get
23692383
{
2370-
return MemberDataHelper.Combine(new[] { new object[] { CredentialTypes.ApiKey.VerifyV1, new[] { NuGetScopes.PackageVerify } } }, Returns403IfScopeDoesNotMatch_Data);
2384+
return MemberDataHelper.Combine(
2385+
new[] { new object[] { CredentialTypes.ApiKey.VerifyV1, new[] { NuGetScopes.PackageVerify } } },
2386+
Returns403IfScopeDoesNotMatch_Data,
2387+
Returns403IfScopeDoesNotMatch_PackageVersion_Data);
23712388
}
23722389
}
23732390

23742391
[Theory]
23752392
[MemberData(nameof(Returns403IfScopeDoesNotMatch_NotVerify_Data))]
23762393
[MemberData(nameof(Returns403IfScopeDoesNotMatch_Verify_Data))]
2377-
public async Task Returns403IfScopeDoesNotMatch(string credentialType, string[] expectedRequestedActions, ApiScopeEvaluationResult apiScopeEvaluationResult, HttpStatusCode expectedStatusCode, string description)
2394+
public async Task Returns403IfScopeDoesNotMatch(string credentialType, string[] expectedRequestedActions, ApiScopeEvaluationResult apiScopeEvaluationResult, HttpStatusCode expectedStatusCode, string description, string packageVersion)
23782395
{
23792396
// Arrange
2397+
PackageVersion = packageVersion;
23802398
var package = new Package
23812399
{
23822400
PackageRegistration = new PackageRegistration() { Id = PackageId },

tests/NuGetGallery.Facts/Framework/MemberDataHelper.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,25 @@ public static IEnumerable<object[]> AsDataSet(params object[] dataSet)
1818
return dataSet.Select(d => new[] { d });
1919
}
2020

21-
public static IEnumerable<object[]> Combine(IEnumerable<object[]> firstDataSet, IEnumerable<object[]> secondDataSet)
21+
public static IEnumerable<object[]> Combine(params IEnumerable<object[]>[] dataSets)
2222
{
23+
if (!dataSets.Any())
24+
{
25+
yield break;
26+
}
27+
28+
var firstDataSet = dataSets.First();
29+
var lastDataSet = Combine(dataSets.Skip(1).ToArray()).ToArray();
2330
foreach (var firstData in firstDataSet)
2431
{
25-
foreach (var secondData in secondDataSet)
32+
foreach (var lastData in lastDataSet)
33+
{
34+
yield return firstData.Concat(lastData).ToArray();
35+
}
36+
37+
if (!lastDataSet.Any())
2638
{
27-
yield return firstData.Concat(secondData).ToArray();
39+
yield return firstData;
2840
}
2941
}
3042
}

0 commit comments

Comments
 (0)