Skip to content

Commit afb9219

Browse files
authored
Add reCAPTCHA to package uploads (#8948)
1 parent d87a5ba commit afb9219

15 files changed

Lines changed: 122 additions & 14 deletions

File tree

src/AccountDeleter/EmptyFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,5 +285,10 @@ public bool ProxyGravatarEnSubdomain()
285285
{
286286
throw new NotImplementedException();
287287
}
288+
289+
public bool IsRecaptchaEnabledForUploads()
290+
{
291+
throw new NotImplementedException();
292+
}
288293
}
289294
}

src/GitHubVulnerabilities2Db/Fakes/FakeFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,5 +284,10 @@ public bool IsRecentPackagesNoIndexEnabled()
284284
{
285285
throw new NotImplementedException();
286286
}
287+
288+
public bool IsRecaptchaEnabledForUploads()
289+
{
290+
throw new NotImplementedException();
291+
}
287292
}
288293
}

src/NuGetGallery.Services/Configuration/FeatureFlagService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class FeatureFlagService : IFeatureFlagService
5555
private const string DisplayTargetFrameworkFeatureName = GalleryPrefix + "DisplayTargetFramework";
5656
private const string ComputeTargetFrameworkFeatureName = GalleryPrefix + "ComputeTargetFramework";
5757
private const string RecentPackagesNoIndexFeatureName = GalleryPrefix + "RecentPackagesNoIndex";
58+
private const string RecaptchaEnabledForUploadsName = GalleryPrefix + "RecaptchaForUploads";
5859

5960
private const string ODataV1GetAllNonHijackedFeatureName = GalleryPrefix + "ODataV1GetAllNonHijacked";
6061
private const string ODataV1GetAllCountNonHijackedFeatureName = GalleryPrefix + "ODataV1GetAllCountNonHijacked";
@@ -374,5 +375,10 @@ public bool IsRecentPackagesNoIndexEnabled()
374375
{
375376
return _client.IsEnabled(RecentPackagesNoIndexFeatureName, defaultValue: false);
376377
}
378+
379+
public bool IsRecaptchaEnabledForUploads()
380+
{
381+
return _client.IsEnabled(RecaptchaEnabledForUploadsName, defaultValue: false);
382+
}
377383
}
378384
}

src/NuGetGallery.Services/Configuration/IFeatureFlagService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,5 +301,10 @@ public interface IFeatureFlagService
301301
/// Whether or not recent packages has no index applied to block search engine indexing.
302302
/// </summary>
303303
bool IsRecentPackagesNoIndexEnabled();
304+
305+
/// <summary>
306+
/// Whether or not we have reCAPTCHA enabled for package uploads
307+
/// </summary>
308+
bool IsRecaptchaEnabledForUploads();
304309
}
305310
}

src/NuGetGallery/App_Data/Files/Content/flags.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"NuGetGallery.MarkdigMdRendering": "Enabled",
3232
"NuGetGallery.ImageAllowlist": "Enabled",
3333
"NuGetGallery.ShowReportAbuseSafetyChanges": "Enabled",
34-
"NuGetGallery.ComputeTargetFramework": "Enabled"
34+
"NuGetGallery.ComputeTargetFramework": "Enabled",
35+
"NuGetGallery.RecaptchaForUploads": "Disabled"
3536
},
3637
"Flights": {
3738
"NuGetGallery.TyposquattingFlight": {

src/NuGetGallery/App_Start/DefaultDependenciesModule.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using Autofac;
2121
using Autofac.Core;
2222
using Autofac.Extensions.DependencyInjection;
23+
using Autofac.Integration.Mvc;
2324
using Elmah;
2425
using Microsoft.ApplicationInsights.Extensibility;
2526
using Microsoft.ApplicationInsights.Extensibility.Implementation;
@@ -47,8 +48,8 @@
4748
using NuGetGallery.Cookies;
4849
using NuGetGallery.Diagnostics;
4950
using NuGetGallery.Features;
51+
using NuGetGallery.Filters;
5052
using NuGetGallery.Frameworks;
51-
using NuGetGallery.Helpers;
5253
using NuGetGallery.Infrastructure;
5354
using NuGetGallery.Infrastructure.Authentication;
5455
using NuGetGallery.Infrastructure.Lucene;
@@ -509,6 +510,8 @@ protected override void Load(ContainerBuilder builder)
509510

510511
RegisterCookieComplianceService(configuration, loggerFactory);
511512

513+
RegisterCustomMvcFilters(builder, configuration);
514+
512515
builder.RegisterType<CookieExpirationService>()
513516
.As<ICookieExpirationService>()
514517
.SingleInstance();
@@ -860,6 +863,17 @@ private static void RegisterFeatureFlagsService(ContainerBuilder builder, Config
860863
.SingleInstance();
861864
}
862865

866+
private static void RegisterCustomMvcFilters(ContainerBuilder builder, ConfigurationService configuration)
867+
{
868+
#pragma warning disable CS4014 // VerifyPackage is not awaited because this is attachment, not execution
869+
builder
870+
.Register(context => new ValidateRecaptchaResponseForUploadsAttribute(context.Resolve<IFeatureFlagService>()))
871+
.AsActionFilterFor<PackagesController>(controller => controller.VerifyPackage(default(VerifyPackageRequest)));
872+
#pragma warning restore CS4014 // VerifyPackage is not awaited because this is attachment, not execution
873+
874+
builder.RegisterFilterProvider();
875+
}
876+
863877
private static void RegisterMessagingService(ContainerBuilder builder, ConfigurationService configuration)
864878
{
865879
if (configuration.Current.AsynchronousEmailServiceEnabled)

src/NuGetGallery/Controllers/PackagesController.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ private async Task<ActionResult> UploadSymbolsPackageInternal(SubmitPackageReque
326326
var verifyRequest = new VerifyPackageRequest(packageMetadata, accountsAllowedOnBehalfOf, existingPackageRegistration);
327327
verifyRequest.IsSymbolsPackage = true;
328328
verifyRequest.HasExistingAvailableSymbols = packageForUploadingSymbols.IsLatestSymbolPackageAvailable();
329+
verifyRequest.RecaptchaEnabled = false; // No need for recaptcha for symbols packages
329330

330331
model.InProgressUpload = verifyRequest;
331332

@@ -373,6 +374,7 @@ private async Task<ActionResult> UploadPackageInternal(SubmitPackageRequest mode
373374
verifyRequest.LicenseFileContents = await GetLicenseFileContentsOrNullAsync(packageMetadata, packageArchiveReader);
374375
verifyRequest.LicenseExpressionSegments = GetLicenseExpressionSegmentsOrNull(packageMetadata.LicenseMetadata);
375376
verifyRequest.ReadmeFileContents = await GetReadmeFileContentsOrNullAsync(packageMetadata, packageArchiveReader);
377+
verifyRequest.RecaptchaEnabled = _featureFlagService.IsRecaptchaEnabledForUploads();
376378

377379
model.InProgressUpload = verifyRequest;
378380
return View(model);
@@ -2405,6 +2407,7 @@ public virtual ActionResult CancelPendingOwnershipRequest(string id, string requ
24052407
return Redirect(Url.ManagePackageOwnership(id));
24062408
}
24072409

2410+
/// Note that for Recaptcha filtering we attach ValidateRecaptchaResponseForUploadsAttribute directly here: <see cref="DefaultDependenciesModule.RegisterCustomMvcFilters" />.
24082411
[UIAuthorize]
24092412
[HttpPost]
24102413
[RequiresAccountConfirmation("upload a package")]

src/NuGetGallery/Filters/ValidateRecaptchaResponseAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace NuGetGallery.Filters
1313
{
1414
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
15-
public sealed class ValidateRecaptchaResponseAttribute : ActionFilterAttribute
15+
public class ValidateRecaptchaResponseAttribute : ActionFilterAttribute
1616
{
1717
private const string RecaptchaResponseId = "g-recaptcha-response";
1818
private const string RecaptchaValidationUrl = "https://www.google.com/recaptcha/api/siteverify?secret={0}&response={1}";
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Web.Mvc;
6+
7+
namespace NuGetGallery.Filters
8+
{
9+
[AttributeUsage(AttributeTargets.Method)]
10+
public sealed class ValidateRecaptchaResponseForUploadsAttribute : ValidateRecaptchaResponseAttribute
11+
{
12+
private readonly IFeatureFlagService _featureFlagService;
13+
14+
public ValidateRecaptchaResponseForUploadsAttribute(IFeatureFlagService featureFlagService) => _featureFlagService = featureFlagService;
15+
16+
public override void OnActionExecuting(ActionExecutingContext filterContext)
17+
{
18+
if (_featureFlagService.IsRecaptchaEnabledForUploads())
19+
{
20+
base.OnActionExecuting(filterContext);
21+
}
22+
}
23+
}
24+
}

src/NuGetGallery/NuGetGallery.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@
220220
<Compile Include="Extensions\CakeBuildManagerExtensions.cs" />
221221
<Compile Include="Extensions\ImageExtensions.cs" />
222222
<Compile Include="Filters\AdminActionAttribute.cs" />
223+
<Compile Include="Filters\ValidateRecaptchaResponseForUploadsAttribute.cs" />
223224
<Compile Include="Helpers\AdminHelper.cs" />
224225
<Compile Include="Helpers\ValidationHelper.cs" />
225226
<Compile Include="Helpers\ViewModelExtensions\DeleteAccountListPackageItemViewModelFactory.cs" />

0 commit comments

Comments
 (0)