Skip to content

Commit 6ffe196

Browse files
authored
Add admin panel for bulk ownership changes (#9070)
Spec: NuGet/Engineering#3998 Issue: NuGet/Engineering#3994
1 parent 14c6db7 commit 6ffe196

25 files changed

Lines changed: 1150 additions & 208 deletions

src/AccountDeleter/Configuration/GalleryConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,5 +114,6 @@ public string SiteRoot
114114
public int? MinIoThreads { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
115115
public int? MaxIoThreads { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
116116
public string InternalMicrosoftTenantKey { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
117+
public string AdminSenderUser { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
117118
}
118119
}

src/Bootstrap/dist/css/bootstrap-theme.css

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
.page-admin-index {
22
h2 {
33
margin-bottom: 8px;
4-
margin-top: 32px;
4+
margin-top: 8px;
5+
font-size: 30px;
6+
7+
.ms-Icon {
8+
font-size: 2.4rem;
9+
top: 2px;
10+
}
11+
}
12+
13+
p {
14+
margin-bottom: 16px;
515
}
616
}

src/NuGetGallery.Services/Authentication/AsyncFileUpload/AsyncFileUploadModule.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,6 @@ private void ReadStream(Stream stream, HttpRequest request, string uploadKey, As
8787
}
8888

8989
_cacheService.SetProgress(uploadKey, progress);
90-
91-
#if DEBUG
92-
if (request.IsLocal)
93-
{
94-
// If the request is from local machine, the upload will be too fast to see the progress.
95-
// Slow it down a bit.
96-
System.Threading.Thread.Sleep(30);
97-
}
98-
#endif
9990
}
10091
}
10192

src/NuGetGallery.Services/Configuration/AppConfiguration.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,5 +418,6 @@ public string ExternalBrandingMessage
418418
[DefaultValue(null)]
419419
public int? MaxIoThreads { get; set; }
420420
public string InternalMicrosoftTenantKey { get; set; }
421+
public string AdminSenderUser { get; set; }
421422
}
422423
}

src/NuGetGallery.Services/Configuration/IAppConfiguration.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,5 +501,14 @@ public interface IAppConfiguration : IMessageServiceConfiguration
501501
/// https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/7w2sway1(v=vs.100)?redirectedfrom=MSDN
502502
/// </summary>
503503
int? MaxIoThreads { get; set; }
504+
505+
/// <summary>
506+
/// The username of the user that can be entered as the sender for admin flows. When an admin flow may send one
507+
/// or more emails to end users, it helps to mask the identity of the site admin that performed the action by
508+
/// using this user instead. This account is not created automatically. The username should refer to a user
509+
/// account (not organization) that has an email address that can be visible for administrative notices. This
510+
/// account should not have any credentials or be marked as a site admin.
511+
/// </summary>
512+
string AdminSenderUser { get; set; }
504513
}
505514
}

src/NuGetGallery.Services/PackageManagement/IPackageOwnershipManagementService.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Threading.Tasks;
67
using NuGet.Services.Entities;
@@ -54,17 +55,21 @@ public interface IPackageOwnershipManagementService
5455
/// <param name="packageRegistration">The package registration that is intended to get ownership.</param>
5556
/// <param name="requestingUser">The user requesting to remove an owner from the package.</param>
5657
/// <param name="userToBeRemoved">The user to remove as an owner from the package.</param>
57-
Task RemovePackageOwnerWithMessagesAsync(PackageRegistration packageRegistration, User requestingUser, User userToBeRemoved);
58+
/// <param name="requireNamespaceOwnership">Whether or not to verify that the <paramref name="requestingUser"/> has permissions for any reserved namespaces that the package is in.</param>
59+
/// <exception cref="InvalidOperationException">Thrown if <paramref name="requireNamespaceOwnership"/> is true and the <paramref name="requestingUser"/> does not have permissions for an existing namespace.</exception>
60+
Task RemovePackageOwnerWithMessagesAsync(PackageRegistration packageRegistration, User requestingUser, User userToBeRemoved, bool requireNamespaceOwnership);
5861

5962
/// <summary>
6063
/// Remove the user as from the list of owners of the package. Also remove the package registration
6164
/// from the reserved namespaces owned by this user if the Id matches any of the reserved prefixes
6265
/// and the user is the only package owner that owns the namespace that matches the package registration.
66+
/// This method verifies that the <paramref name="requestingUser"/> has the permissions to an existing namespaces.
6367
/// </summary>
6468
/// <param name="packageRegistration">The package registration that is intended to get ownership.</param>
6569
/// <param name="requestingUser">The user requesting to remove an owner from the package.</param>
6670
/// <param name="userToBeRemoved">The user to remove as an owner from the package.</param>
6771
/// <param name="commitChanges">Whether or not to commit the changes.</param>
72+
/// <exception cref="InvalidOperationException">Thrown if the <paramref name="requestingUser"/> does not have permissions for an existing namespace.</exception>
6873
Task RemovePackageOwnerAsync(PackageRegistration packageRegistration, User requestingUser, User userToBeRemoved, bool commitChanges);
6974

7075
/// <summary>

src/NuGetGallery.Services/PackageManagement/PackageOwnershipManagementService.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -247,15 +247,25 @@ public IEnumerable<PackageOwnerRequest> GetPackageOwnershipRequests(PackageRegis
247247
return _packageOwnerRequestService.GetPackageOwnershipRequests(package, requestingOwner, newOwner);
248248
}
249249

250-
public async Task RemovePackageOwnerWithMessagesAsync(PackageRegistration packageRegistration, User requestingOwner, User ownerToBeRemoved)
250+
public async Task RemovePackageOwnerWithMessagesAsync(PackageRegistration packageRegistration, User requestingOwner, User ownerToBeRemoved, bool requireNamespaceOwnership)
251251
{
252-
await RemovePackageOwnerAsync(packageRegistration, requestingOwner, ownerToBeRemoved);
252+
await RemovePackageOwnerAsync(packageRegistration, requestingOwner, ownerToBeRemoved, commitChanges: true, requireNamespaceOwnership);
253253

254254
var emailMessage = new PackageOwnerRemovedMessage(_appConfiguration, requestingOwner, ownerToBeRemoved, packageRegistration);
255255
await _messageService.SendMessageAsync(emailMessage);
256256
}
257257

258258
public async Task RemovePackageOwnerAsync(PackageRegistration packageRegistration, User requestingOwner, User ownerToBeRemoved, bool commitChanges = true)
259+
{
260+
await RemovePackageOwnerAsync(packageRegistration, requestingOwner, ownerToBeRemoved, commitChanges, requireNamespaceOwnership: true);
261+
}
262+
263+
private async Task RemovePackageOwnerAsync(
264+
PackageRegistration packageRegistration,
265+
User requestingOwner,
266+
User ownerToBeRemoved,
267+
bool commitChanges,
268+
bool requireNamespaceOwnership)
259269
{
260270
if (packageRegistration == null)
261271
{
@@ -272,7 +282,7 @@ public async Task RemovePackageOwnerAsync(PackageRegistration packageRegistratio
272282
throw new ArgumentNullException(nameof(ownerToBeRemoved));
273283
}
274284

275-
if (OwnerHasPermissionsToRemove(requestingOwner, ownerToBeRemoved, packageRegistration))
285+
if (!requireNamespaceOwnership || OwnerHasPermissionsToRemoveFromNamespace(requestingOwner, ownerToBeRemoved, packageRegistration))
276286
{
277287
if (commitChanges)
278288
{
@@ -390,7 +400,7 @@ private async Task DeletePackageOwnershipRequestAsync(PackageRegistration packag
390400
}
391401
}
392402

393-
private static bool OwnerHasPermissionsToRemove(User requestingOwner, User ownerToBeRemoved, PackageRegistration packageRegistration)
403+
private static bool OwnerHasPermissionsToRemoveFromNamespace(User requestingOwner, User ownerToBeRemoved, PackageRegistration packageRegistration)
394404
{
395405
var reservedNamespaces = packageRegistration.ReservedNamespaces.ToList();
396406
if (ActionsRequiringPermissions.AddPackageToReservedNamespace

src/NuGetGallery/Areas/Admin/Controllers/HomeController.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public virtual ActionResult Index()
2424
{
2525
var viewModel = new HomeViewModel(
2626
showDatabaseAdmin: _config.Current.AdminPanelDatabaseAccessEnabled,
27+
showLuceneAdmin: _config.Current.SearchServiceUriPrimary == null && _config.Current.SearchServiceUriSecondary == null,
2728
showValidation: _config.Current.AsynchronousPackageValidationEnabled);
2829

2930
return View(viewModel);

0 commit comments

Comments
 (0)